green-button-data 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +13 -5
  2. data/README.md +116 -8
  3. data/green-button-data.gemspec +6 -1
  4. data/lib/green-button-data.rb +26 -1
  5. data/lib/green-button-data/application_information.rb +112 -0
  6. data/lib/green-button-data/authorization.rb +35 -0
  7. data/lib/green-button-data/configuration.rb +133 -0
  8. data/lib/green-button-data/core_ext.rb +1 -0
  9. data/lib/green-button-data/core_ext/string.rb +35 -0
  10. data/lib/green-button-data/dst.rb +57 -0
  11. data/lib/green-button-data/entry.rb +87 -0
  12. data/lib/green-button-data/feed.rb +5 -0
  13. data/lib/green-button-data/fetchable.rb +242 -0
  14. data/lib/green-button-data/interval_block.rb +38 -0
  15. data/lib/green-button-data/local_time_parameters.rb +31 -0
  16. data/lib/green-button-data/meter_reading.rb +4 -0
  17. data/lib/green-button-data/model_collection.rb +33 -0
  18. data/lib/green-button-data/parser/application_information.rb +2 -11
  19. data/lib/green-button-data/parser/authorization.rb +0 -4
  20. data/lib/green-button-data/parser/interval.rb +8 -4
  21. data/lib/green-button-data/parser/interval_reading.rb +4 -1
  22. data/lib/green-button-data/parser/local_time_parameters.rb +0 -57
  23. data/lib/green-button-data/parser/summary_measurement.rb +12 -0
  24. data/lib/green-button-data/reading_type.rb +82 -0
  25. data/lib/green-button-data/relations.rb +24 -0
  26. data/lib/green-button-data/usage_point.rb +27 -0
  27. data/lib/green-button-data/usage_summary.rb +36 -0
  28. data/lib/green-button-data/utilities.rb +27 -0
  29. data/lib/green-button-data/version.rb +1 -1
  30. data/spec/fixtures.rb +5 -0
  31. data/spec/fixtures/ESPIReadingType.xml +23 -535
  32. data/spec/fixtures/ESPIReadingTypes.xml +535 -0
  33. data/spec/fixtures/ESPIUsagePoint.xml +3 -3
  34. data/spec/fixtures/ESPIUsagePointMeterReading.xml +13 -0
  35. data/spec/fixtures/ESPIUsagePointMeterReadings.xml +21 -0
  36. data/spec/fixtures/ESPIUsagePoints.xml +822 -0
  37. data/spec/lib/green-button-data/application_information_spec.rb +389 -0
  38. data/spec/lib/green-button-data/authorization_spec.rb +91 -0
  39. data/spec/{green-button-data → lib/green-button-data}/core_ext/date_spec.rb +0 -0
  40. data/spec/{green-button-data → lib/green-button-data}/core_ext/fixnum_spec.rb +0 -0
  41. data/spec/lib/green-button-data/local_time_parameters_spec.rb +106 -0
  42. data/spec/{green-button-data → lib/green-button-data}/parser/application_information_spec.rb +4 -4
  43. data/spec/{green-button-data → lib/green-button-data}/parser/authorization_spec.rb +0 -12
  44. data/spec/{green-button-data → lib/green-button-data}/parser/entry_spec.rb +1 -1
  45. data/spec/{green-button-data → lib/green-button-data}/parser/interval_block_spec.rb +4 -4
  46. data/spec/{green-button-data → lib/green-button-data}/parser/local_time_parameter_spec.rb +0 -0
  47. data/spec/{green-button-data → lib/green-button-data}/parser/reading_type_spec.rb +0 -0
  48. data/spec/{green-button-data → lib/green-button-data}/parser/usage_point_spec.rb +0 -0
  49. data/spec/{green-button-data → lib/green-button-data}/parser/usage_summary_spec.rb +0 -0
  50. data/spec/lib/green-button-data/reading_type_spec.rb +127 -0
  51. data/spec/lib/green-button-data/usage_point_spec.rb +167 -0
  52. data/spec/{green-button-data → lib/green-button-data}/utilities_spec.rb +1 -1
  53. data/spec/spec_helper.rb +5 -0
  54. metadata +70 -17
@@ -0,0 +1,38 @@
1
+ module GreenButtonData
2
+ class IntervalBlock < Entry
3
+ include Utilities
4
+
5
+ attr_accessor :length, :starts_at, :ends_at, :duration
6
+
7
+ def initialize(attributes)
8
+ super
9
+
10
+ @starts_at = @interval.starts_at local: true
11
+ @ends_at = @interval.ends_at local: true
12
+ @duration = @interval.duration
13
+ @length = @interval_readings.size
14
+ end
15
+
16
+ ##
17
+ # Returns an array representation of all the interval data
18
+ def to_a
19
+ result = []
20
+
21
+ @interval_readings.each do |interval_reading|
22
+ reading = {
23
+ starts_at: interval_reading.time_period.starts_at(local: true),
24
+ ends_at: interval_reading.time_period.ends_at(local: true),
25
+ duration: interval_reading.time_period.duration,
26
+ value: interval_reading.value
27
+ }
28
+
29
+ reading[:cost] = interval_reading.cost if interval_reading.cost
30
+ reading[:quality] = interval_reading.quality if interval_reading.quality
31
+
32
+ result << reading
33
+ end
34
+
35
+ return result
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module GreenButtonData
2
+ class LocalTimeParameters < Entry
3
+ include Dst
4
+
5
+ attr_accessor :dst_offset, :tz_offset
6
+
7
+ def dst_starts_at(year = Time.now.year)
8
+ byte_to_dst_datetime(@dst_start_rule, year).to_time
9
+ end
10
+
11
+ def dst_ends_at(year = Time.now.year)
12
+ byte_to_dst_datetime(@dst_end_rule, year).to_time
13
+ end
14
+
15
+ def total_offset
16
+ @dst_offset + @tz_offset
17
+ end
18
+
19
+ def to_h
20
+ {
21
+ dst: {
22
+ starts_at: dst_starts_at,
23
+ ends_at: dst_ends_at,
24
+ offset: dst_offset
25
+ },
26
+ tz_offset: tz_offset,
27
+ total_offset: total_offset
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ module GreenButtonData
2
+ class MeterReading < Entry
3
+ end
4
+ end
@@ -0,0 +1,33 @@
1
+ module GreenButtonData
2
+ class ModelCollection
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @models = []
7
+ end
8
+
9
+ def <<(model)
10
+ @models << model
11
+ end
12
+
13
+ def each
14
+ return enum_for(:each) unless block_given?
15
+
16
+ @models.each do |model|
17
+ yield model
18
+ end
19
+ end
20
+
21
+ def last
22
+ @models.last
23
+ end
24
+
25
+ def size
26
+ @models.size
27
+ end
28
+
29
+ def find_by_id(id)
30
+ self.find {|model| model.id.to_s == id.to_s }
31
+ end
32
+ end
33
+ end
@@ -44,17 +44,8 @@ module GreenButtonData
44
44
  element :client_name
45
45
  element :client_id
46
46
  element :client_secret
47
- element :client_id_issued_at, class: Integer do |epoch|
48
- Time.at(normalize_epoch(epoch)).utc.to_datetime
49
- end
50
- element :client_secret_expires_at, class: Integer do |epoch|
51
- if epoch == 0
52
- # 0 means don't expire; set it to distant future
53
- DateTime.new 9999, 12, 31, 23, 59, 59
54
- else
55
- Time.at(normalize_epoch(epoch)).utc.to_datetime
56
- end
57
- end
47
+ element :client_id_issued_at, class: Integer
48
+ element :client_secret_expires_at, class: Integer
58
49
  element :redirect_uri
59
50
  element :software_id
60
51
  element :software_version
@@ -13,10 +13,6 @@ module GreenButtonData
13
13
 
14
14
  element :status, class: Integer
15
15
 
16
- def active?
17
- @status > 0
18
- end
19
-
20
16
  # TODO: Add scope parser
21
17
  element :scope
22
18
 
@@ -12,12 +12,16 @@ module GreenButtonData
12
12
  normalize_epoch t
13
13
  end
14
14
 
15
- def starts_at
16
- Time.at(normalize_epoch(@start)).utc.to_datetime
15
+ def starts_at(kwargs = {})
16
+ epoch_to_time @start, kwargs
17
17
  end
18
18
 
19
- def ends_at
20
- Time.at(normalize_epoch(@start + @duration)).utc.to_datetime
19
+ def ends_at(kwargs = {})
20
+ epoch_to_time @start + @duration, kwargs
21
+ end
22
+
23
+ def to_s
24
+ "#{starts_at} - #{ends_at}"
21
25
  end
22
26
 
23
27
  # Standard ESPI namespacing
@@ -4,7 +4,10 @@ module GreenButtonData
4
4
  include SAXMachine
5
5
  include Enumerations
6
6
 
7
- element :cost, class: Integer
7
+ element :cost, class: Integer do |cost|
8
+ cost / 100000.0
9
+ end
10
+
8
11
  element :quality, class: Integer
9
12
  element :timePeriod, class: Interval, as: :time_period
10
13
  element :value, class: Integer
@@ -43,63 +43,6 @@ module GreenButtonData
43
43
  element :'ns0:dstEndRule', as: :dst_end_rule
44
44
  element :'ns0:dstOffset', class: Integer, as: :dst_offset
45
45
  element :'ns0:tzOffset', class: Integer, as: :tz_offset
46
-
47
- private
48
-
49
- def byte_to_dst_datetime(byte, year = Time.now.year)
50
- # Bits 0 - 11: seconds 0 - 3599
51
- seconds = byte & BITMASK_SECOND
52
-
53
- # Bits 12 - 16: hours 0 - 23
54
- hour = (byte & BITMASK_HOUR) >> BITSHIFT_HOUR
55
-
56
- # Bits 17 - 19: day of the week; 0 = NA, 1 - 7 (Monday = 1)
57
- weekday = (byte & BITMASK_DAY_OF_WEEK) >> BITSHIFT_DAY_OF_WEEK
58
-
59
- # Bits 20 - 24: day of the month; 0 = NA, 1 - 31
60
- day = (byte & BITMASK_DAY_OF_MONTH) >> BITSHIFT_DAY_OF_MONTH
61
-
62
- # Bits 25 - 27: DST rule 0 - 7
63
- dst_rule = (byte & BITMASK_DST_RULE) >> BITSHIFT_DST_RULE
64
-
65
- # Bits 28 - 31: month 1 - 12
66
- month = (byte & BITMASK_MONTH) >> BITSHIFT_MONTH
67
-
68
- # Raise an error unless all the values are in valid range
69
- seconds.between?(0, 3599) and hour.between?(0, 23) and
70
- weekday.between?(1, 7) and day.between?(0, 31) and
71
- dst_rule.between?(0, 7) and month.between?(1, 12) or
72
- raise RangeError, 'Invalid value range'
73
-
74
- # In Ruby, Sunday = 0 not 7
75
- weekday = weekday == 7 ? 0 : weekday
76
-
77
- # Check the DST rule
78
- dst_day = if dst_rule == 1
79
- # Rule 1: DST starts/ends on Day of Week on or after the Day of Month
80
- day_of_month = DateTime.new year, month, day
81
- day_offset = if weekday >= day_of_month.wday
82
- weekday - day_of_month.wday
83
- else
84
- 7 + weekday - day_of_month.wday
85
- end
86
-
87
- day_of_month + day_offset
88
- elsif dst_rule.between?(2, 6)
89
- # Rule 2 - 6: DST starts/ends on Nth Day of Week in given month
90
- # Nth Day of Week (e.g. third Friday of July)
91
- nth_weekday_of year, month, weekday, dst_rule - 1
92
- elsif dst_rule == 7
93
- # Rule 7: DST starts/ends on last Day of Week in given month
94
- last_weekday_of year, month, weekday
95
- else
96
- # Rule 0: DST starts/ends on the Day of Month
97
- DateTime.new year, month, day
98
- end
99
-
100
- # Add the hour and seconds component to the day
101
- dst_day + Rational(hour * 3600 + seconds, 86400)
102
- end
103
46
  end
104
47
  end
105
48
  end
@@ -19,6 +19,18 @@ module GreenButtonData
19
19
  UNIT_SYMBOL[@uom]
20
20
  end
21
21
 
22
+ def raw_value
23
+ @value
24
+ end
25
+
26
+ def value
27
+ @value * 10.0 ** @power_of_ten_multiplier
28
+ end
29
+
30
+ def to_s
31
+ "#{value} #{uom.to_s}"
32
+ end
33
+
22
34
  # ESPI Namespacing
23
35
  element :'espi:powerOfTenMultiplier', class: Integer,
24
36
  as: :power_of_ten_multiplier
@@ -0,0 +1,82 @@
1
+ module GreenButtonData
2
+ class ReadingType < Entry
3
+ include Enumerations
4
+
5
+ attr_reader :consumption_tier, :cpp, :tou
6
+
7
+ def accumulation_behaviour
8
+ get_enum_symbol ACCUMULATION, @accumulation_behaviour
9
+ end
10
+
11
+ def commodity
12
+ get_enum_symbol COMMODITY, @commodity
13
+ end
14
+
15
+ def currency
16
+ get_enum_symbol CURRENCY, @currency
17
+ end
18
+
19
+ def data_qualifier
20
+ get_enum_symbol DATA_QUALIFIER, @data_qualifier
21
+ end
22
+
23
+ def default_quality
24
+ get_enum_symbol QUALITY_OF_READING, @default_quality
25
+ end
26
+
27
+ def flow_direction
28
+ get_enum_symbol FLOW_DIRECTION, @flow_direction
29
+ end
30
+
31
+ def interval_length
32
+ @interval_length
33
+ end
34
+
35
+ def kind
36
+ get_enum_symbol MEASUREMENT, @kind
37
+ end
38
+
39
+ def measuring_period
40
+ get_enum_symbol TIME_ATTRIBUTE, @measuring_period
41
+ end
42
+
43
+ def phase
44
+ get_enum_symbol PHASE_CODE, @phase
45
+ end
46
+
47
+ def scale_factor
48
+ 10.0 ** @power_of_ten_multiplier
49
+ end
50
+
51
+ def time_attribute
52
+ get_enum_symbol TIME_PERIOD_OF_INTEREST, @time_attribute
53
+ end
54
+
55
+ def unit_of_measurement
56
+ get_enum_symbol UNIT_SYMBOL, @uom
57
+ end
58
+
59
+ alias_method :unit, :unit_of_measurement
60
+ alias_method :uom, :unit_of_measurement
61
+
62
+ def to_h
63
+ {
64
+ accumulation_behaviour: accumulation_behaviour,
65
+ commodity: commodity,
66
+ consumption_tier: consumption_tier,
67
+ cpp: cpp,
68
+ currency: currency,
69
+ data_qualifier: data_qualifier,
70
+ default_quality: default_quality,
71
+ flow_direction: flow_direction,
72
+ kind: kind,
73
+ measuring_period: measuring_period,
74
+ phase: phase,
75
+ scale_factor: scale_factor,
76
+ time_attribute: time_attribute,
77
+ tou: tou,
78
+ unit_of_measurement: unit_of_measurement
79
+ }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,24 @@
1
+ module GreenButtonData
2
+ module Relations
3
+
4
+ ##
5
+ # Extracts related URLs from an Entry
6
+ #
7
+ # ==== Arguments
8
+ #
9
+ # * +entry+ - An instance of GreenButtonData::Parser::Entry
10
+ def construct_related_urls(entry)
11
+ related_urls = {}
12
+
13
+ entry.related.each do |related_url|
14
+ match_data = /\/(\w+)(\/(\d+))*$/.match(related_url)
15
+
16
+ unless match_data.nil?
17
+ related_urls[:"#{match_data[1].underscore}"] = related_url
18
+ end
19
+ end
20
+
21
+ related_urls
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module GreenButtonData
2
+ class UsagePoint < Entry
3
+ include Enumerations
4
+
5
+ attr_accessor :service_category
6
+
7
+ def initialize(attributes)
8
+ super
9
+
10
+ # If deprecated usage summary name is used, create an alias
11
+ if self.class.instance_methods.grep(/electric_power_usage/).size > 0
12
+ warn "DEPRECATED: ElectricPowerUsageSummary has been renamed to UsageSummary"
13
+ singleton_class.class_eval do
14
+ alias_method :usage_summaries, :electric_power_usage_summaries
15
+ end
16
+ end
17
+ end
18
+
19
+ def service_category
20
+ SERVICE[@kind]
21
+ end
22
+
23
+ def usage_summary_url
24
+ return @usage_summary_url || @electric_power_usage_summary_url
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ module GreenButtonData
2
+ class UsageSummary < Entry
3
+ include Enumerations
4
+ include Utilities
5
+
6
+ attr_reader :billing_period,
7
+ :overall_consumption_last_period
8
+
9
+ attr_writer :commodity,
10
+ :quality_of_reading
11
+
12
+ def commodity
13
+ if @commodity.is_a? Numeric
14
+ COMMODITY[@commodity]
15
+ elsif @commodity.is_a? Symbol
16
+ @commodity
17
+ end
18
+ end
19
+
20
+ def quality_of_reading
21
+ if @quality_of_reading.is_a? Numeric
22
+ QUALITY_OF_READING[@quality_of_reading]
23
+ elsif @quality_of_reading.is_a? Symbol
24
+ @quality_of_reading
25
+ end
26
+ end
27
+
28
+ def status_timestamp(kwargs = {})
29
+ epoch_to_time @status_time_stamp, kwargs
30
+ end
31
+
32
+ def to_s
33
+ "#{@billing_period}: #{@overall_consumption_last_period}"
34
+ end
35
+ end
36
+ end
@@ -34,6 +34,16 @@ module GreenButtonData
34
34
  end
35
35
  end
36
36
 
37
+ def epoch_to_time(epoch, kwargs = {})
38
+ time = Time.at normalize_epoch(epoch)
39
+
40
+ if kwargs[:local] == true
41
+ return time.localtime
42
+ else
43
+ return time.utc
44
+ end
45
+ end
46
+
37
47
  ##
38
48
  # Retrieves the first Sunday of the month
39
49
  #
@@ -102,5 +112,22 @@ module GreenButtonData
102
112
 
103
113
  last_day - day_offset
104
114
  end
115
+
116
+ ##
117
+ # Returns a hash representation of object instance's attributes
118
+ #
119
+ # ==== Arguments
120
+ #
121
+ # * +obj+ - an object
122
+ def attributes_to_hash(obj)
123
+ attributes_hash = {}
124
+
125
+ obj.instance_variables.each do |var|
126
+ attr_name = var.to_s.delete('@').to_sym
127
+ attributes_hash[attr_name] = obj.instance_variable_get(var)
128
+ end
129
+
130
+ return attributes_hash
131
+ end
105
132
  end
106
133
  end