movingsign_api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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