simms 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require <%= File.join(File.expand_path(File.dirname(__FILE__)), 'spec', 'spec_helper.rb') %>
@@ -0,0 +1,7 @@
1
+ == 1.0.0 / 2012-03-02
2
+
3
+ * Initial revision
4
+ * Support for stream bytes parsing
5
+ * Support for packet parsing
6
+ * Support for global config, electricity config and electricity data beacons
7
+ * Full rspec coverage
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Irongaze Consulting LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ = GEM: simms
2
+
3
+ Written by Rob Morris @ Irongaze Consulting LLC (http://irongaze.com)
4
+
5
+ Based on sample PHP code and documentation provided by Simms Energy (http://simmsenergy.com)
6
+
7
+ Funding for development provided by Sustainable Industrial Solutions LLC (http://sustainableis.com)
8
+
9
+ == DESCRIPTION
10
+
11
+ A set of classes to enable reading posted data from Simms Energy monitoring products (http://simmsenergy.com)
12
+
13
+ == SYNOPSIS
14
+
15
+ This gem facilitates interpreting incoming monitoring data and converting it into Ruby objects for easier
16
+ manipulation. It handles parsing the byte-stream of mesh data Simms devices provide, including
17
+ conversion of hex byte-codes into Ruby native data types such as integers, bools and floats.
18
+
19
+ To use:
20
+
21
+ # Require the library (not needed if using Rails/bundler)
22
+ >> require 'simms'
23
+
24
+ # Parse a packet to extract the contained beacon, passing in
25
+ # the timestamp and data posted to your server from the Simms device
26
+ >> beacon = Simms::Packet.parse(params['DateTime'], params['MeshData'])
27
+
28
+ # Which device sent this beacon?
29
+ >> beacon.uuid
30
+ => '0A-28-00-48-27-88'
31
+
32
+ # See what kind of beacon we have
33
+ >> beacon.group
34
+ => :electric
35
+ >> beacon.type
36
+ => :data
37
+ >> beacon.data?
38
+ => true
39
+
40
+ # Extract some data from the beacon
41
+ >> beacon.phase
42
+ => 0
43
+ >> beacon.accumulated_real_energy
44
+ => 15290
45
+ >> beacon.present_real_power
46
+ => 472.08821
47
+
48
+ == REQUIREMENTS
49
+
50
+ None, though you'll need rspec to build and test the gem
51
+
52
+ == INSTALL
53
+
54
+ To install, simply run:
55
+
56
+ sudo gem install simms
57
+
58
+ RVM users can skip the sudo:
59
+
60
+ gem install simms
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,8 @@
1
+ # Load our dependencies
2
+ #require 'iron/extensions'
3
+
4
+ # Requires all classes
5
+ search_path = File.join(File.expand_path(File.dirname(__FILE__)), '*', '*.rb')
6
+ Dir.glob(search_path) do |path|
7
+ require path
8
+ end
@@ -0,0 +1,81 @@
1
+ # Represents the data or configuration information broadcast by a single Simms monitoring
2
+ # device.
3
+ module Simms
4
+ class Beacon
5
+
6
+ # Attributes
7
+ attr_accessor :uuid, :timestamp, :command_group_id, :device_command_id
8
+
9
+ # Command group codes
10
+ CG_GLOBAL = 0xFFFF
11
+ CG_SENSOR = 0x0001
12
+ CG_ELECTRIC = 0x0002
13
+ CG_HVAC = 0x0004
14
+ CG_PULSER = 0x0005
15
+ CG_IRRIGATION = 0x0007
16
+
17
+ # Device commands
18
+ DC_DATA = 0x0001
19
+ DC_GLOBAL_CONFIG = 0x000B
20
+ DC_CONFIG = 0x0022
21
+
22
+ # Find the appropriate beacon class for a given group + command tuple
23
+ def self.class_for(group, command)
24
+ case group
25
+
26
+ when CG_GLOBAL then
27
+ case command
28
+ when DC_GLOBAL_CONFIG then
29
+ return Simms::GlobalConfigBeacon
30
+ end
31
+
32
+ when CG_ELECTRIC then
33
+ case command
34
+ when DC_DATA then
35
+ return Simms::ElectricityDataBeacon
36
+ when DC_CONFIG then
37
+ return Simms::ElectricityConfigBeacon
38
+ end
39
+
40
+ end
41
+
42
+ # None found!
43
+ nil
44
+ end
45
+
46
+ def initialize(bytes)
47
+ @bytes = bytes
48
+ end
49
+
50
+ def group
51
+ case @command_group_id
52
+ when CG_GLOBAL then :global
53
+ when CG_SENSOR then :sensor
54
+ when CG_ELECTRIC then :electric
55
+ when CG_HVAC then :hvac
56
+ when CG_PULSER then :pulser
57
+ when CG_IRRIGATION then :irrigation
58
+ else
59
+ :unknown
60
+ end
61
+ end
62
+
63
+ def type
64
+ case @device_command_id
65
+ when DC_DATA then :data
66
+ when DC_CONFIG, DC_GLOBAL_CONFIG then :config
67
+ else
68
+ :unknown
69
+ end
70
+ end
71
+
72
+ def data?
73
+ type == :data
74
+ end
75
+
76
+ def config?
77
+ type == :config
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,73 @@
1
+ # Manages a set of byte codes in hex string format (eg 'AF', '0C', etc) and
2
+ # provides methods for extracting floats, ints, etc from that stream by
3
+ # position.
4
+ #
5
+ # Responsible for low-level translation of incoming data into types usable
6
+ # in Ruby.
7
+ module Simms
8
+ class Bytes
9
+
10
+ attr_accessor :data
11
+
12
+ def initialize(byte_array)
13
+ @data = byte_array
14
+ @data = @data.to_s.scan(/../) unless @data.is_a?(Array)
15
+ end
16
+
17
+ def count
18
+ @data.count
19
+ end
20
+
21
+ def empty?
22
+ count == 0
23
+ end
24
+
25
+ def uint8(pos)
26
+ @data[pos].hex
27
+ end
28
+
29
+ def uint16(pos)
30
+ @data[pos...pos+2].reverse.join('').hex
31
+ end
32
+
33
+ def sint16(pos)
34
+ [@data[pos...pos+2].join].pack('H*').unpack('s').first
35
+ end
36
+
37
+ def uint32(pos)
38
+ @data[pos...pos+4].reverse.join('').hex
39
+ end
40
+
41
+ def sint32(pos)
42
+ [@data[pos...pos+4].join].pack('H*').unpack('l').first
43
+ end
44
+
45
+ def bool(pos)
46
+ @data[pos].hex == 1
47
+ end
48
+
49
+ def float(pos)
50
+ v = uint32(pos)
51
+ x = (v & ((1 << 23) - 1)) + (1 << 23) * (v >> 31 | 1)
52
+ exp = (v >> 23 & 0xFF) - 127
53
+ return x.to_f * (2 ** (exp - 23))
54
+ end
55
+
56
+ def slice(*args)
57
+ Bytes.new(@data.slice(*args))
58
+ end
59
+
60
+ def [](*args)
61
+ Bytes.new(@data[*args])
62
+ end
63
+
64
+ def calc_checksum
65
+ @data.inject(0) {|sum, byte| sum + byte.hex} % 0xFF
66
+ end
67
+
68
+ def to_s
69
+ @data.join('')
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,34 @@
1
+ require 'simms/beacon'
2
+
3
+ module Simms
4
+ class ElectricityConfigBeacon < Simms::Beacon
5
+
6
+ def ct_amps
7
+ @bytes.uint16(6)
8
+ end
9
+
10
+ def nominal_voltage
11
+ @bytes.uint16(8)
12
+ end
13
+
14
+ def phase_type
15
+ @bytes.uint8(10)
16
+ end
17
+
18
+ # Human-readable description of the phase type
19
+ def phase_type_description
20
+ case phase_type
21
+ when 0 then
22
+ '3 Phase With Neutral, Display L-N'
23
+ when 1 then
24
+ '3 Phase With Neutral, Display L-L'
25
+ when 2 then
26
+ 'Single Split Phase A+B with Neutral, Display L-N'
27
+ when 3 then
28
+ 'Single Phase A to Neutral, Display L-N'
29
+ else
30
+ 'Unknown phase type!'
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,70 @@
1
+ require 'simms/beacon'
2
+
3
+ # Electricity data beacons are sent at the time interval configured in the device, and will be
4
+ # have one packet sent per phase being monitored, ie for a 3-phase system, you will receive
5
+ # 3 data beacons per reporting interval per device, with the phase field set to 0, 1, or 2.
6
+ module Simms
7
+ class ElectricityDataBeacon < Simms::Beacon
8
+
9
+ # Which phase this beacon represents
10
+ def phase
11
+ @bytes.uint8(6)
12
+ end
13
+
14
+ # TODO What does this mean? What are the valid values?
15
+ def error_state
16
+ @bytes.uint8(7)
17
+ end
18
+
19
+ # May be negative, as in the case of a solar power installation. Is a counter, ie to get
20
+ # the real energy (Wh) for a given interval, you need to diff against past values
21
+ def accumulated_real_energy
22
+ @bytes.sint32(8)
23
+ end
24
+
25
+ # In Wh - see #accumulated_real_energy for counter info
26
+ def accumulated_apparent_energy
27
+ @bytes.sint32(12)
28
+ end
29
+
30
+ # In W
31
+ def present_real_power
32
+ @bytes.float(16)
33
+ end
34
+
35
+ # In W
36
+ def present_apparent_power
37
+ @bytes.float(20)
38
+ end
39
+
40
+ def volts_rms
41
+ @bytes.float(24)
42
+ end
43
+
44
+ def current_rms
45
+ @bytes.float(28)
46
+ end
47
+
48
+ # In VA
49
+ def power_factor
50
+ @bytes.float(32)
51
+ end
52
+
53
+ def max_demand
54
+ @bytes.float(36)
55
+ end
56
+
57
+ def volts_sag
58
+ @bytes.uint16(40)
59
+ end
60
+
61
+ def volts_peak
62
+ @bytes.uint16(42)
63
+ end
64
+
65
+ def current_peak
66
+ @bytes.uint16(44)
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,29 @@
1
+ require 'simms/beacon'
2
+
3
+ module Simms
4
+ class GlobalConfigBeacon < Simms::Beacon
5
+
6
+ def version_high
7
+ @bytes.uint16(6)
8
+ end
9
+
10
+ def version_low
11
+ @bytes.uint16(8)
12
+ end
13
+
14
+ def firmware_version
15
+ "#{version_high}.#{version_low}"
16
+ end
17
+
18
+ def local_devices
19
+ @bytes.uint16(10)
20
+ end
21
+
22
+ def device_name
23
+ name = ''
24
+ @bytes.slice(24,16).data.collect {|b| b.hex}.each {|c| name << c if c > 0}
25
+ name
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ module Simms
2
+ class Packet
3
+
4
+ # Action type constants
5
+ ACTION_BEACON = 0x35
6
+
7
+ # Parses an incoming packet, extracts its enclosed beacon data, instantiates
8
+ # the correct beacon class, and returns it. Raises exceptions on malformed data.
9
+ # If beacon type is unknown, raises an exception if raise_on_unknown is set to true. Otherwise,
10
+ # unknown beacons are returned as nil.
11
+ def self.parse(timestamp, data_packet, raise_on_unknown = false)
12
+ # Parse the string into hex pair bytes for the mesh packet
13
+ mesh_data = Bytes.new(data_packet)
14
+
15
+ # Extract the bytes for the embedded beacon data
16
+ beacon_data = mesh_data[34..-1]
17
+
18
+ # Verify data
19
+ if mesh_data.empty? || beacon_data.empty?
20
+ raise "Malformed packet - missing segments"
21
+ end
22
+ unless mesh_data.uint16(32) == beacon_data.count
23
+ raise "Malformed packet - length field (#{mesh_data.uint16(32)}) does not match data length (#{beacon_data.count})"
24
+ end
25
+
26
+ # Parse timestamp - always UTC time
27
+ timestamp = Time.utc(*(timestamp.split(/[\- \:]/)))
28
+
29
+ # Get key variables from packet
30
+ uuid = mesh_data.slice(6,6).data.join('-')
31
+ group = beacon_data.uint16(0)
32
+ command = beacon_data.uint8(2)
33
+ action = beacon_data.uint8(3)
34
+
35
+ # Instantiate beacon instance (assuming it's a beacon action!)
36
+ klass = Simms::Beacon.class_for(group, command) if action == ACTION_BEACON
37
+ if klass
38
+ # Create the beacon, then set all the meta-info we have about it
39
+ beacon = klass.new(beacon_data)
40
+ beacon.command_group_id = group
41
+ beacon.device_command_id = command
42
+ beacon.timestamp = timestamp
43
+ beacon.uuid = uuid
44
+ beacon
45
+
46
+ else
47
+ raise "Unable to find beacon class for group #{group} and command #{command}" if raise_on_unknown
48
+ nil
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,136 @@
1
+
2
+ describe Simms::Beacon do
3
+
4
+ SAMPLE_TIMESTAMP = '2012-06-27 16:16:08'
5
+ SAMPLE_GC_BEACON = 'FB10040002000015ABA0B0220100FF00FF00FFFFFFFFFFFF0100790E937900022A00FFFF0B351A820000C8000100040002000015ABA0B022010053696D6D734F666669636550616E656C0000'
6
+ SAMPLE_EC_BEACON = 'FB10040002000015ABA0B0250100FF00FF00FFFFFFFFFFFF0100AA72704C00020D00020022359FCA960078000300D3'
7
+ SAMPLE_ED_BEACON = 'FB10040002000015ABA0B0220100FF00FF00FFFFFFFFFFFF01008E51CB1C00022E0002000135E58900014EF722004308280041E91144132620442D5EF542D417A740B43D693F8C8820447A007A000500'
8
+
9
+ it 'should return the correct class for a given group + command combo' do
10
+ {
11
+ [0xFFFF, 0x0B] => Simms::GlobalConfigBeacon,
12
+ [0x0002, 0x01] => Simms::ElectricityDataBeacon,
13
+ [0x0002, 0x22] => Simms::ElectricityConfigBeacon
14
+ }.each do |info, klass|
15
+ Simms::Beacon.class_for(*info).should == klass
16
+ end
17
+ end
18
+
19
+ context 'when a global configuration beacon' do
20
+ before do
21
+ @beacon = Simms::Packet.parse(SAMPLE_TIMESTAMP, SAMPLE_GC_BEACON)
22
+ end
23
+
24
+ it 'should know what kind of beacon it is' do
25
+ @beacon.group.should == :global
26
+ @beacon.type.should == :config
27
+ @beacon.config?.should be_true
28
+ end
29
+
30
+ it 'should extract firmware version' do
31
+ @beacon.firmware_version.should == '0.200'
32
+ end
33
+
34
+ it 'should extract local device count' do
35
+ @beacon.local_devices.should == 1
36
+ end
37
+
38
+ it 'should extract device name' do
39
+ @beacon.device_name.should == 'SimmsOfficePanel'
40
+ end
41
+ end
42
+
43
+ context 'when an electricity configuration beacon' do
44
+ before do
45
+ @beacon = Simms::Packet.parse(SAMPLE_TIMESTAMP, SAMPLE_EC_BEACON)
46
+ end
47
+
48
+ it 'should know what kind of beacon it is' do
49
+ @beacon.group.should == :electric
50
+ @beacon.type.should == :config
51
+ @beacon.config?.should be_true
52
+ end
53
+
54
+ it 'should extract CT clamp amps' do
55
+ @beacon.ct_amps.should == 150
56
+ end
57
+
58
+ it 'should extract nominal voltage' do
59
+ @beacon.nominal_voltage.should == 120
60
+ end
61
+
62
+ it 'should extract phase type' do
63
+ @beacon.phase_type.should == 3
64
+ end
65
+
66
+ it 'should provide a user-readable phase type' do
67
+ @beacon.phase_type_description.should == 'Single Phase A to Neutral, Display L-N'
68
+ end
69
+ end
70
+
71
+ context 'when an electricity data beacon' do
72
+ before do
73
+ @beacon = Simms::Packet.parse(SAMPLE_TIMESTAMP, SAMPLE_ED_BEACON)
74
+ end
75
+
76
+ it 'should know what kind of beacon it is' do
77
+ @beacon.group.should == :electric
78
+ @beacon.type.should == :data
79
+ @beacon.data?.should be_true
80
+ end
81
+
82
+ it 'should extract the phase' do
83
+ @beacon.phase.should == 0
84
+ end
85
+
86
+ it 'should extract the error state' do
87
+ @beacon.error_state.should == 1
88
+ end
89
+
90
+ it 'should extract the accumulated real energy (Wh)' do
91
+ @beacon.accumulated_real_energy.should == 2291534
92
+ end
93
+
94
+ it 'should extract the accumulated apparent energy' do
95
+ @beacon.accumulated_apparent_energy.should == 2623555
96
+ end
97
+
98
+ it 'should extract the present real power' do
99
+ @beacon.present_real_power.round(4).should == 583.6446
100
+ end
101
+
102
+ it 'should extract the present apparent power' do
103
+ @beacon.present_apparent_power.round(4).should == 640.5949
104
+ end
105
+
106
+ it 'should extract the volts rms' do
107
+ @beacon.volts_rms.round(4).should == 122.6839
108
+ end
109
+
110
+ it 'should extract the current rms' do
111
+ @beacon.current_rms.round(4).should == 5.2217
112
+ end
113
+
114
+ it 'should extract the power factor' do
115
+ @beacon.power_factor.round(4).should == 0.9111
116
+ end
117
+
118
+ it 'should extract the max demand' do
119
+ @beacon.max_demand.round(4).should == 642.1335
120
+ end
121
+
122
+ it 'should extract the volts sag' do
123
+ @beacon.volts_sag.should == 122
124
+ end
125
+
126
+ it 'should extract the volts peak' do
127
+ @beacon.volts_peak.should == 122
128
+ end
129
+
130
+ it 'should extract the current peak' do
131
+ @beacon.current_peak.should == 5
132
+ end
133
+
134
+ end
135
+
136
+ end
@@ -0,0 +1,47 @@
1
+ describe Simms::Bytes do
2
+
3
+ it 'should manage a set of byte strings' do
4
+ bytes = bytes('010200ABCD')
5
+ bytes.count.should == 5
6
+ bytes.data.last.should == 'CD'
7
+ end
8
+
9
+ it 'should extract unsigned 8 bit ints' do
10
+ bytes('01').uint8(0).should == 1
11
+ bytes('AC').uint8(0).should == 172
12
+ bytes('FF').uint8(0).should == 255
13
+ end
14
+
15
+ it 'should extract unsigned 16 bit ints' do
16
+ bytes('0400').uint16(0).should == 4
17
+ bytes('0000').uint16(0).should == 0
18
+ bytes('FFFF').uint16(0).should == 65535
19
+ end
20
+
21
+ it 'should extract unsigned 32 bit ints' do
22
+ bytes('4EF72200').uint32(0).should == 2291534
23
+ bytes('FFFFFFFF').uint32(0).should == 4294967295
24
+ end
25
+
26
+ it 'should extract signed 32 bit ints' do
27
+ bytes('4EF72200').sint32(0).should == 2291534
28
+ bytes('FFFFFFFF').sint32(0).should == -1
29
+ end
30
+
31
+ it 'should extract floats' do
32
+ bytes('8C882044').float(0).round(4).should == 642.1335
33
+ bytes('B43D693F').float(0).round(4).should == 0.9111
34
+ end
35
+
36
+ it 'should enable subset extraction' do
37
+ @bytes = bytes('1234567890')
38
+ @bytes[0].data.should == ['12']
39
+ @bytes[1,2].to_s.should == '3456'
40
+ end
41
+
42
+ # Helper to construct a new Bytes object from a given string
43
+ def bytes(str)
44
+ Simms::Bytes.new(str)
45
+ end
46
+
47
+ end
@@ -0,0 +1,30 @@
1
+ describe Simms::Packet do
2
+
3
+ SAMPLE_TIMESTAMP = '2012-06-27 16:16:08'
4
+ SAMPLE_PACKET = 'FB10040002000015ABA0B0220100FF00FF00FFFFFFFFFFFF0100790E937900022A00FFFF0B351A820000C8000100040002000015ABA0B022010053696D6D734F666669636550616E656C0000'
5
+
6
+ before(:all) do
7
+ @beacon = Simms::Packet.parse(SAMPLE_TIMESTAMP, SAMPLE_PACKET)
8
+ end
9
+
10
+ it 'should parse valid packets' do
11
+ Simms::Packet.parse(SAMPLE_TIMESTAMP, SAMPLE_PACKET).should_not be_nil
12
+ end
13
+
14
+ it 'should return the correct beacon type' do
15
+ @beacon.should be_a(Simms::GlobalConfigBeacon)
16
+ end
17
+
18
+ it 'should correctly set the beacon\'s device UUID' do
19
+ @beacon.uuid.should == '00-15-AB-A0-B0-22'
20
+ end
21
+
22
+ it 'should correctly set the beacon\'s timestamp' do
23
+ @beacon.timestamp.should == Time.utc(2012,6,27,16,16,8)
24
+ end
25
+
26
+ it 'should return raise on invalid packet data' do
27
+ lambda{Simms::Packet.parse(SAMPLE_TIMESTAMP, '1234')}.should raise_error
28
+ end
29
+
30
+ end
@@ -0,0 +1,9 @@
1
+ # Require our library
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'simms'))
3
+
4
+ RSpec.configure do |config|
5
+ #config.add_formatter 'documentation'
6
+ config.color = true
7
+ config.backtrace_clean_patterns = [/rspec/]
8
+ end
9
+
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simms
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rob Morris
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.6'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.6'
30
+ description: Allows accepting and parsing Simms Energy monitor data streams.
31
+ email:
32
+ - rob@irongaze.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/simms/beacon.rb
38
+ - lib/simms/bytes.rb
39
+ - lib/simms/electricity_config_beacon.rb
40
+ - lib/simms/electricity_data_beacon.rb
41
+ - lib/simms/global_config_beacon.rb
42
+ - lib/simms/packet.rb
43
+ - lib/simms.rb
44
+ - spec/simms/beacon_spec.rb
45
+ - spec/simms/bytes_spec.rb
46
+ - spec/simms/packet_spec.rb
47
+ - spec/spec_helper.rb
48
+ - LICENSE
49
+ - History.txt
50
+ - Version.txt
51
+ - README.rdoc
52
+ - .rspec
53
+ homepage: http://irongaze.com
54
+ licenses:
55
+ - MIT
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 1.9.2
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 1.8.24
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Data stream parser for Simms Energy monitors
78
+ test_files: []