movingsign_api 0.0.1

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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/.yardopts +1 -0
  6. data/CHANGELOG.md +11 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +60 -0
  10. data/Rakefile +1 -0
  11. data/lib/movingsign_api/commands/command.rb +76 -0
  12. data/lib/movingsign_api/commands/hard_reset_command.rb +16 -0
  13. data/lib/movingsign_api/commands/internal/align_mode.rb +50 -0
  14. data/lib/movingsign_api/commands/internal/display_mode.rb +61 -0
  15. data/lib/movingsign_api/commands/internal/display_pause.rb +33 -0
  16. data/lib/movingsign_api/commands/internal/display_speed.rb +45 -0
  17. data/lib/movingsign_api/commands/internal/file_handle.rb +54 -0
  18. data/lib/movingsign_api/commands/internal/pretty_keyable.rb +50 -0
  19. data/lib/movingsign_api/commands/internal/sender_receiver_address.rb +82 -0
  20. data/lib/movingsign_api/commands/internal/utilities.rb +8 -0
  21. data/lib/movingsign_api/commands/set_clock_command.rb +32 -0
  22. data/lib/movingsign_api/commands/set_sound_command.rb +21 -0
  23. data/lib/movingsign_api/commands/software_reset_command.rb +16 -0
  24. data/lib/movingsign_api/commands/write_control_command.rb +35 -0
  25. data/lib/movingsign_api/commands/write_text_command.rb +116 -0
  26. data/lib/movingsign_api/errors.rb +22 -0
  27. data/lib/movingsign_api/sign.rb +144 -0
  28. data/lib/movingsign_api/version.rb +3 -0
  29. data/lib/movingsign_api.rb +13 -0
  30. data/movingsign_api.gemspec +30 -0
  31. data/spec/align_mode_spec.rb +28 -0
  32. data/spec/commands/set_sound_command_spec.rb +19 -0
  33. data/spec/display_mode_spec.rb +57 -0
  34. data/spec/display_speed_spec.rb +23 -0
  35. data/spec/examples/example_A_spec.rb +24 -0
  36. data/spec/examples/example_C_spec.rb +16 -0
  37. data/spec/examples/example_D_spec.rb +14 -0
  38. data/spec/examples/example_K_spec.rb +14 -0
  39. data/spec/examples/expected.rb +130 -0
  40. data/spec/file_handle_spec.rb +17 -0
  41. data/spec/sender_receiver_address_spec.rb +60 -0
  42. data/spec/spec_helper.rb +19 -0
  43. data/spec/tutorials/readme_1.rb +5 -0
  44. data/spec/tutorials/readme_2.rb +5 -0
  45. data/spec/tutorials/tutorials_spec.rb +14 -0
  46. metadata +188 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e0a1ad2325eb6653f274649048c8cc87521342cf
4
+ data.tar.gz: a6c42e582bd97653f85baf448a85db94bed5eaa9
5
+ SHA512:
6
+ metadata.gz: abef5a49210a9f01bc090de1eb78d5979b0ca2e2969bf45b947309b096a3c047158754c4aee7c6f19e3876ffb752239fb6c93f4138a073f6cf92d9d1b33f93c5
7
+ data.tar.gz: c1e6bddf3f7a8c86e48dc5f5b1936a7f3713aa7a2099b69a69db5fa4b9720f2d8f174432106c57c863f83ae6bc5e8ac46716587b6fe06508504c6211954f0d86
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *~
2
+ /temp.rb
3
+
4
+ # Gem/Bundler Defaults
5
+
6
+ *.gem
7
+ *.rbc
8
+ .bundle
9
+ .config
10
+ .yardoc
11
+ Gemfile.lock
12
+ InstalledFiles
13
+ _yardoc
14
+ coverage
15
+ doc/
16
+ lib/bundler/man
17
+ pkg
18
+ rdoc
19
+ spec/reports
20
+ test/tmp
21
+ test/version_tmp
22
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.0
5
+ before_script:
6
+ - gem install bundler --version "~> 1.3"
7
+ - bundle install
8
+ script: bundle exec rspec -fd
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ - README.md CHANGELOG.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Version History
2
+
3
+ ## Version 0.0.1
4
+
5
+ #### Added
6
+
7
+ * Write text command
8
+ * Set date/time command
9
+ * Software reset command
10
+ * Hard reset command
11
+ * Set sound command
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in movingsign_api.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Eric Webb
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # MovingsignApi
2
+
3
+ MovingSign Communication Protocol V2.1 Implementation in Ruby to control
4
+ [compatible LED signs](http://www.signsdirect.com/Home/LED-Signs-Programmable/7x80-LED-Indoor-Brightness-Sign-Red.html).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'movingsign_api'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install movingsign_api
19
+
20
+ ## Usage
21
+
22
+ Using the MovingsignApi is straight forward.
23
+
24
+ <!-- include spec/tutorials/readme_1.rb -->
25
+ ``` ruby
26
+ require 'movingsign_api'
27
+
28
+ sign = MovingsignApi::Sign.new '/dev/ttyUSB0'
29
+
30
+ sign.show_text "Hello World"
31
+
32
+ ```
33
+
34
+ There are other commands and quite a few options. See [MovingsignApi::Sign](lib/movingsign_api/sign.rb) or [MovingsignApi::Command](lib/movingsign_api/commands/command.rb) and it's subclasses.
35
+
36
+ ## Versions
37
+
38
+ A complete version history is in [CHANGELOG.md](CHANGELOG.md).
39
+
40
+ ## Todo
41
+
42
+ Not all of the Movingsign protocol is implemented. Some of missing functionality include:
43
+
44
+ * Text formatting isn't supported
45
+ * Graphics commands aren't supported
46
+ * Some write control commands are not implemented:
47
+ * Set/Change password
48
+ * Set/Change device address
49
+ * Changing the text file display mode
50
+ * Read control commands
51
+ * Read clock
52
+ * Read equipment attributes
53
+
54
+ ## Contributing
55
+
56
+ Make a pull request and be sure to include test cases!
57
+
58
+ ## Other Projects
59
+
60
+ * [multi_movingsign](https://github.com/webmonarch/multi_movingsign) - to drive multiple Movingsigns at the same time for an information display board
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,76 @@
1
+ require 'movingsign_api/commands/internal/sender_receiver_address'
2
+ require 'movingsign_api/commands/internal/utilities'
3
+
4
+ module MovingsignApi
5
+ # Command class, subclassed by each MovingsignApi class
6
+ #
7
+ # When subclassing, be sure to implement {#command_code} and {#command_payload_bytes}
8
+ #
9
+ # Also see some useful subclasses: {WriteTextCommand}, {WriteControlCommand}
10
+ class Command
11
+ include Utilities
12
+
13
+ # The sender's address. See {SenderReceiverAddress}
14
+ attr_accessor :sender
15
+ # The receiving sign's address. See {SenderReceiverAddress}
16
+ attr_accessor :receiver
17
+
18
+ def sender=(val)
19
+ @sender = MovingsignApi::SenderReceiverAddress.parse(val)
20
+ end
21
+
22
+ def receiver=(val)
23
+ @receiver = MovingsignApi::SenderReceiverAddress.parse(val)
24
+ end
25
+
26
+ # Returns the command identifier string. See specification for list of valid command codes.
27
+ #
28
+ # @return [String]
29
+ def command_code
30
+ raise MovingsignApi::NotImplementedError, "Needs to be implemented in subclass."
31
+ end
32
+
33
+ # Returns a byte array representing this command, appropriate for sending to the sign's serial port
34
+ #
35
+ # @return [Array<Byte>]
36
+ def to_bytes
37
+ # set defaults
38
+ self.sender ||= :pc
39
+ self.receiver ||= 1
40
+
41
+ bytes = []
42
+
43
+ bytes.concat [0x00] * 5 # start of command
44
+ bytes.concat [0x01] # <SOH>
45
+ bytes.concat self.sender.to_bytes # Sender Address
46
+ bytes.concat self.receiver.to_bytes # Reciver Address
47
+ bytes.concat [0x02] # <STX>
48
+ bytes.concat string_to_ascii_bytes(command_code) # Command Code
49
+ bytes.concat command_payload_bytes # command specific payload
50
+ bytes.concat [0x03] # <ETX>
51
+ bytes.concat generate_checksum_bytes(bytes[10..-1]) # Checksum bytes (4)
52
+ bytes.concat [0x04] # <EOT>
53
+
54
+ bytes
55
+ end
56
+
57
+ private
58
+
59
+ # Returns command specific byte array payload
60
+ #
61
+ # @return [Array<Byte>]
62
+ def command_payload_bytes
63
+ raise MovingsignApi::NotImplementedError, "Needs to be implemented in subclass."
64
+ end
65
+
66
+ # Returns a checksum string (4 characters) appropriate for sending to the serial port
67
+ #
68
+ # ie: '12AF' (note capitalization)
69
+ def generate_checksum_bytes(payload)
70
+ sum = payload.reduce :+
71
+ sum_hex = ('%04x' % sum).upcase
72
+
73
+ string_to_ascii_bytes sum_hex
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,16 @@
1
+ require 'movingsign_api/commands/write_control_command'
2
+
3
+ module MovingsignApi
4
+ # Performs a hard reset, <b>DELETING ALL DATA ON THE SIGN</b>
5
+ #
6
+ # @note The sign will restart after receiving this command
7
+ class HardResetCommand < WriteControlCommand
8
+ def subcommand_code
9
+ 'L'
10
+ end
11
+
12
+ def subcommand_payload_bytes
13
+ []
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,50 @@
1
+ require 'movingsign_api/commands/internal/pretty_keyable'
2
+
3
+ module MovingsignApi
4
+ # Align mode for a sign.
5
+ #
6
+ # Valid values are:
7
+ # * +:left+ or +'1'+
8
+ # * +:center+ or +'2'+
9
+ # * +:right+ or +'3+
10
+ class AlignMode
11
+ include PrettyKeyable
12
+
13
+ # @return [Symbol] align mode (one of {#left}, {#right}, {#center})
14
+ attr_accessor :key
15
+
16
+ protected
17
+
18
+ # Registers a specific align mode key and value combo
19
+ def self.align_mode(key, code)
20
+ register key, code
21
+ end
22
+
23
+ public
24
+
25
+ # @!group Align Mode Constants
26
+ # @!macro [attach] dm.align_mode
27
+ # @!attribute [r] $1
28
+ # Align mode +:$1+ (protocol align mode +'$2'+)
29
+ # @return [Symbol] +:$1+
30
+ align_mode :left, '1'
31
+ align_mode :right, '3'
32
+ align_mode :center, '2'
33
+ # @!endgroup
34
+
35
+ # @param key [Symbol] one of the valid alignment keys
36
+ def initialize(key)
37
+ @key = key
38
+ end
39
+
40
+ # Parses an symbol or string into a valid {AlignMode} instance
41
+ # @return [AlignMode]
42
+ def self.parse(input)
43
+ if key = parse_to_key(input)
44
+ self.new key
45
+ else
46
+ raise InvalidInputError, "Align mode '#{input}' is invalid."
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ require 'movingsign_api/commands/internal/pretty_keyable'
2
+
3
+ module MovingsignApi
4
+ # Text display mode setting.
5
+ class DisplayMode
6
+ include PrettyKeyable
7
+
8
+ # @!visibility private
9
+ def self.display_mode(key, code)
10
+ register key, code
11
+ end
12
+
13
+ # @!group Display Mode Constants
14
+ # @!macro [attach] dm.display_mode
15
+ # @!attribute [r] $1
16
+ # Display mode $1 (protocol display mode +'$2'+)
17
+ # @return [Symbol] +:$1+
18
+ display_mode :auto, 'A'
19
+ display_mode :flash, 'B'
20
+ display_mode :hold, 'C'
21
+ display_mode :interlock, 'D'
22
+ display_mode :rolldown, 'E'
23
+ display_mode :rollup, 'F'
24
+ display_mode :rollin, 'G'
25
+ display_mode :rollout, 'H'
26
+ display_mode :rollleft, 'I'
27
+ display_mode :rollright, 'J'
28
+ display_mode :rotate, 'K'
29
+ display_mode :slide, 'L'
30
+ display_mode :snow, 'M'
31
+ display_mode :sparkle, 'N'
32
+ display_mode :spray, 'O'
33
+ display_mode :starburst, 'P'
34
+ display_mode :switch, 'Q'
35
+ display_mode :twinkle, 'R'
36
+ display_mode :wipedown, 'S'
37
+ display_mode :wipeup, 'T'
38
+ display_mode :wipein, 'U'
39
+ display_mode :wipeout, 'V'
40
+ display_mode :wipeleft, 'W'
41
+ display_mode :wiperight, 'X'
42
+ display_mode :cyclecolor, 'Y'
43
+ display_mode :clock, 'Z'
44
+ # @!endgroup
45
+
46
+ # @return [Symbol] display mode constant (see attributes {#auto}, {#flash}, {#hold}, etc.)
47
+ attr_accessor :key
48
+
49
+ def initialize(mode)
50
+ @key = mode
51
+ end
52
+
53
+ def self.parse(input)
54
+ if key = parse_to_key(input)
55
+ self.new key
56
+ else
57
+ raise InvalidInputError, "Display mode '#{input}' is not valid."
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,33 @@
1
+ require 'movingsign_api/commands/internal/pretty_keyable'
2
+ require 'movingsign_api/commands/internal/utilities'
3
+
4
+ module MovingsignApi
5
+ # Text display pause setting (in seconds, 0 - 9)
6
+ class DisplayPause
7
+ include Utilities
8
+
9
+ # @return [Integer] seconds
10
+ attr_accessor :seconds
11
+
12
+ # @param seconds [Integer] Time to pause (in seconds) (0 - 9)
13
+ def initialize(seconds)
14
+ @seconds = parse_seconds(seconds)
15
+ end
16
+
17
+ def to_bytes
18
+ string_to_ascii_bytes self.seconds
19
+ end
20
+
21
+ private
22
+
23
+ def parse_seconds(input)
24
+ if input.kind_of?(String) && input.match(/\A[0-9]\z/)
25
+ input.to_i
26
+ elsif input.kind_of?(Fixnum) && input.between?(0, 9)
27
+ input
28
+ else
29
+ raise "Pause time '#{input}' is invalid."
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ require 'movingsign_api/commands/internal/pretty_keyable'
2
+
3
+ module MovingsignApi
4
+ # Text display mode speed sending
5
+ class DisplaySpeed
6
+ include PrettyKeyable
7
+
8
+ # @!visibility private
9
+ def self.display_speed(key, code)
10
+ register(key, code)
11
+ end
12
+
13
+ # @!group Display speed constants
14
+
15
+ # @!macro [attach] dm.display_speed
16
+ # @!attribute [r] $1
17
+ # Display speed +:$1+ (protocol display speed +'$2'+)
18
+ # @return [Symbol] +:$1+
19
+ display_speed :fastest, '1'
20
+ display_speed :faster, '2'
21
+ display_speed :normal, '3'
22
+ display_speed :slow, '4'
23
+ display_speed :slower, '5'
24
+
25
+ # @!endgroup
26
+
27
+ # @return [Symbol] Display speed constant, one of {#faster}, {#normal}, {#slow}
28
+ attr_accessor :key
29
+
30
+ def initialize(speed)
31
+ @key = speed
32
+ end
33
+
34
+ # Parses the supplied input into a {DisplaySpeed} instance if possible
35
+ # @raise InvalidInputError on invalid input
36
+ # @return [DisplaySpeed]
37
+ def self.parse(input)
38
+ if key = parse_to_key(input)
39
+ self.new key
40
+ else
41
+ raise InvalidInputError, "Display speed '#{input}' is invalid."
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,54 @@
1
+ require 'movingsign_api/commands/internal/utilities'
2
+
3
+ module MovingsignApi
4
+ # Text file handle
5
+ #
6
+ # Valid values are:
7
+ # * Integer - (0 - 35)
8
+ # * String '0' - '9', 'A' - 'Z'
9
+ class FileHandle
10
+ include Utilities
11
+
12
+ # @return [Integer] the file hander integer
13
+ attr_accessor :handle
14
+
15
+ def initialize(input)
16
+ self.handle = self.class.parse_file_handle(input)
17
+ end
18
+
19
+ # Returns the file handle as an integer when given a file handle string
20
+ def self.code_to_handle(code)
21
+ if code.match /[0-9]/
22
+ code.to_i
23
+ else
24
+ (code.unpack('C')[0] - 'A'.unpack('C')[0]) + 10
25
+ end
26
+ end
27
+
28
+ # Returns a file handle string when given a file handle integer
29
+ def self.handle_to_code(handle)
30
+ if handle.between?(0,9)
31
+ handle.to_s
32
+ else
33
+ (0x41 + handle - 10).chr
34
+ end
35
+ end
36
+
37
+ def to_bytes
38
+ string_to_ascii_bytes self.class.handle_to_code(self.handle)
39
+ end
40
+
41
+ private
42
+
43
+ def self.parse_file_handle(input)
44
+ if input.kind_of?(Fixnum) && input.between?(0, 35)
45
+ input
46
+ elsif input.kind_of?(String) && input.match(/\A[0-9A-Z]\z/)
47
+ code_to_handle(input)
48
+ else
49
+ raise InvalidInputError, "File handle '#{input}' is invalid."
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ require 'movingsign_api/commands/internal/utilities'
2
+
3
+ module MovingsignApi
4
+ module PrettyKeyable
5
+ include Utilities
6
+
7
+ # @!visibility private
8
+ def self.included(other)
9
+ other.class_variable_set(:@@KEYS, [])
10
+ other.class_variable_set(:@@CODES, [])
11
+
12
+ other.extend ClassMethods
13
+ end
14
+
15
+ # @!visibility private
16
+ def to_bytes
17
+ string_to_ascii_bytes self.class.codes[self.class.keys.index(self.key)]
18
+ end
19
+
20
+ private
21
+
22
+ module ClassMethods
23
+ # registers the specified key and code pair as synonymous
24
+ def register(key, code)
25
+ keys << key
26
+ codes << code
27
+
28
+ define_singleton_method(key) do
29
+ key
30
+ end
31
+ end
32
+
33
+ def parse_to_key(input)
34
+ if index = (keys.index(input) || codes.index(input))
35
+ keys[index]
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def keys
42
+ class_variable_get(:@@KEYS)
43
+ end
44
+
45
+ def codes
46
+ class_variable_get(:@@CODES)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,82 @@
1
+ require 'movingsign_api/commands/internal/utilities'
2
+
3
+ module MovingsignApi
4
+ # Represents a sender or receiver identifier for a {Command}
5
+ #
6
+ # A address can be represented as an integer between 0 and 255, or per the spec as a two character hex string: '00' - 'FF'.
7
+ #
8
+ # Valid inputs include:
9
+ # - Integer - +0+ - +255+
10
+ # - Symbol - +:broadcast+ or +:pc+
11
+ # - String - +'00'+ - +'FF'+
12
+ #
13
+ # Some notes:
14
+ #
15
+ # - Broadcast Address: (+:broadcast+, +0+, or +'00'+) is a broadcast identifier and shouldn't be used as a sender
16
+ # - PC Address: (+:pc+, +255+, or +'FF''+) is reserved to represent the "PC address" and shouldn't be used as a recipient
17
+ #
18
+ class SenderReceiverAddress
19
+ include Utilities
20
+
21
+ # @return [Integer] the address repsented as an integer
22
+ attr_accessor :identifier
23
+
24
+ # @param identifier [String] Identifier hex string, or hex string with wildcard
25
+ def initialize(identifier)
26
+ @identifier = identifier
27
+ end
28
+
29
+ # Parses the specified input value returning an appropriate {SenderReceiver} instance or raises.
30
+ #
31
+ # @raise [InvalidInputError] on invalid input
32
+ # @return [SenderReceiverAddress]
33
+ def self.parse(input)
34
+ raise MovingsignApi::InvalidInputError, "nil not allowed" if input.nil?
35
+
36
+ value = nil
37
+ if input.kind_of? Fixnum
38
+ if input.between?(0, 255)
39
+ value = ('%02x' % input).upcase
40
+ else
41
+ raise MovingsignApi::InvalidInputError, "Integer #{input} is out of the valid range."
42
+ end
43
+ elsif input.kind_of? Symbol
44
+ if input == :broadcast
45
+ value = '00'
46
+ elsif input == :pc
47
+ value = 'FF'
48
+ else
49
+ raise MovingsignApi::InvalidInputError, "Symbol :#{input} isn't supported"
50
+ end
51
+ elsif input.kind_of? String
52
+ if (value = input.upcase.match /\A[0-9,A-F\?]{2}\z/)
53
+ value = value[0]
54
+ else
55
+ raise MovingsignApi::InvalidInputError, "Parsing string '#{input}' isn't supported"
56
+ end
57
+ else
58
+ raise MovingsignApi::InvalidInputError, "Parsing '#{input.class}' isn't supported"
59
+ end
60
+
61
+ self.new(value)
62
+ end
63
+
64
+ # Returns true if this represents the special 'broadcast' address
65
+ def broadcast?
66
+ self.identifier == '00'
67
+ end
68
+
69
+ # Returns true if this represents the special 'pc' address
70
+ def pc?
71
+ self.identifier = 'FF'
72
+ end
73
+
74
+ def to_s
75
+ self.identifier
76
+ end
77
+
78
+ def to_bytes
79
+ string_to_ascii_bytes self.identifier
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+ module MovingsignApi
2
+ module Utilities
3
+ # @!visibility private
4
+ def string_to_ascii_bytes(input)
5
+ input.to_s.unpack('C*')
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,32 @@
1
+ require 'movingsign_api/commands/write_control_command'
2
+
3
+ module MovingsignApi
4
+ # Sets the signs date + time
5
+ class SetClockCommand < WriteControlCommand
6
+ # @return [Time]
7
+ attr_accessor :datetime
8
+
9
+ def subcommand_code
10
+ 'A'
11
+ end
12
+
13
+ private
14
+
15
+ def subcommand_payload_bytes
16
+ bytes = []
17
+
18
+ # date
19
+ bytes.concat string_to_ascii_bytes(self.datetime.year)
20
+ bytes.concat string_to_ascii_bytes('%02d' % self.datetime.month)
21
+ bytes.concat string_to_ascii_bytes('%02d' % self.datetime.day)
22
+ # time
23
+ bytes.concat string_to_ascii_bytes('%02d' % self.datetime.hour)
24
+ bytes.concat string_to_ascii_bytes('%02d' % self.datetime.min)
25
+ bytes.concat string_to_ascii_bytes('%02d' % self.datetime.sec)
26
+ # day of week
27
+ bytes.concat string_to_ascii_bytes(self.datetime.wday)
28
+
29
+ bytes
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ require 'movingsign_api/commands/write_control_command'
2
+
3
+ module MovingsignApi
4
+ # Sets the sound on/off property of the sign
5
+ class SetSoundCommand < WriteControlCommand
6
+ attr_accessor :on
7
+
8
+ # @param sound_on [Boolean] +true+ to turn sound on, +false+ otherwise
9
+ def initialize(sound_on)
10
+ @on = sound_on
11
+ end
12
+
13
+ def subcommand_code
14
+ 'J'
15
+ end
16
+
17
+ def subcommand_payload_bytes
18
+ string_to_ascii_bytes(@on ? '1' : '0')
19
+ end
20
+ end
21
+ end