aemo 0.1.39 → 0.1.40

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
  SHA1:
3
- metadata.gz: 393bc9a0f07713e90556d7704bb2d129bc398e1d
4
- data.tar.gz: 9d370f741128cc703d0f4f492508a6455203c5e3
3
+ metadata.gz: 99c92027fe76a3d8367716e2274157b9239c48ee
4
+ data.tar.gz: 70d04cf7ed134fc1ac3aab96d8727e043c19104b
5
5
  SHA512:
6
- metadata.gz: 40d6fc601b3f55f8c02cc4c9753e29e89e366ce4c9fd951bfd5708c1843286f064ffd5cbe24bec58bbdd8d95a4d006a2662943571e6c89501e9a9b0d8e6cc99b
7
- data.tar.gz: 6e716d57f1ab10b2af33e7f70df066cba1142faa5d518cd6b27bb3280d03b3913b370532a1440dfc327f0c16b513accf1648f5d55af9c0983c626b7505a7e5c4
6
+ metadata.gz: 0ed1e45c8450de68f0adccf41f87ccbe0bb6cf44d87c417826f75b2738ecc8c13480eb7e087b5f5b66dfc9f73bbb83d887ed88008a773b76f3c9effd62dd8cd2
7
+ data.tar.gz: 8aadfb1e0b590c4527b01fa21cba54f1b651b78e613b834b86fd21716298612647719a23c6010b0e5abc21bdfacb1ea97becded6de357b058f715dba5c2679fb
data/lib/aemo.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/all'
2
4
  require 'httparty'
3
5
  require 'csv'
@@ -6,9 +8,11 @@ require 'aemo/region.rb'
6
8
  require 'aemo/market.rb'
7
9
  require 'aemo/market/interval.rb'
8
10
  require 'aemo/market/node.rb'
11
+ require 'aemo/meter.rb'
9
12
  require 'aemo/nem12.rb'
10
13
  require 'aemo/nmi.rb'
11
14
  require 'aemo/msats.rb'
15
+ require 'aemo/register.rb'
12
16
  require 'aemo/version.rb'
13
17
 
14
18
  # AEMO Module to encapsulate all AEMO classes
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AEMO
2
4
  class Region
3
5
  DISPATCH_TYPE = ['Generator', 'Load Norm Off', 'Network Service Provider'].freeze
data/lib/aemo/market.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AEMO
2
4
  # AEMO::Market
3
5
  #
@@ -13,7 +15,7 @@ module AEMO
13
15
  # Return the current dispatch dataset for a region
14
16
  #
15
17
  # @param [String, AEMO::Region] region AEMO::Region
16
- # @return [Array<AEMO::Market::Interval>] an array of AEMO::Market::Intervals
18
+ # @return [Array<AEMO::Market::Interval>] the current dispatch data
17
19
  def current_dispatch(region)
18
20
  region = AEMO::Region.new(region) if region.is_a?(String)
19
21
 
@@ -25,7 +27,7 @@ module AEMO
25
27
  # Description of method
26
28
  #
27
29
  # @param [String, AEMO::Region] region AEMO::Region
28
- # @return [Array<AEMO::Market::Interval>] an array of AEMO::Market::Intervals
30
+ # @return [Array<AEMO::Market::Interval>] the current trading data
29
31
  def current_trading(region)
30
32
  region = AEMO::Region.new(region) if region.is_a?(String)
31
33
 
@@ -44,16 +46,19 @@ module AEMO
44
46
  region = AEMO::Region.new(region) if region.is_a?(String)
45
47
 
46
48
  required_data = []
47
- (start..finish).map { |d| { year: d.year, month: d.month } }.uniq.each do |period|
48
- required_data += historic_trading(region, period[:year], period[:month])
49
+ (start..finish).map { |d| { year: d.year, month: d.month } }
50
+ .uniq.each do |period|
51
+ required_data += historic_trading(region, period[:year],
52
+ period[:month])
49
53
  end
50
54
 
51
- required_data.select { |values| values.datetime >= start && values.datetime <= finish }
55
+ required_data.select do |values|
56
+ values.datetime >= start && values.datetime <= finish
57
+ end
52
58
  end
53
59
 
54
60
  # Return an array of historic trading values for a Year, Month and Region
55
- # As per the historical data at
56
- # http://www.aemo.com.au/Electricity/Data/Price-and-Demand/Aggregated-Price-and-Demand-Data-Files/Aggregated-Price-and-Demand-2011-to-2016
61
+ # As per the historical data from AEMO
57
62
  #
58
63
  # @param [String, AEMO::Region] region AEMO::Region
59
64
  # @param [Integer] year The year for the report from AEMO
@@ -62,11 +67,12 @@ module AEMO
62
67
  def historic_trading(region, year, month)
63
68
  region = AEMO::Region.new(region) if region.is_a?(String)
64
69
 
65
- month = sprintf('%02d', month)
70
+ month = Kernel.format('%02d', month)
71
+ url = 'https://aemo.com.au/aemo/data/nem/priceanddemand/' \
72
+ "PRICE_AND_DEMAND_#{year}#{month}_#{region}1.csv"
66
73
 
67
- response = HTTParty.get("http://aemo.com.au/aemo/data/nem/priceanddemand/PRICE_AND_DEMAND_#{year}#{month}_#{region}1.csv")
68
- values = parse_response(response)
69
- values
74
+ response = HTTParty.get(url)
75
+ parse_response(response)
70
76
  end
71
77
 
72
78
  protected
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AEMO
2
4
  module Market
3
5
  # AEMO::Market::Interval
@@ -14,7 +16,7 @@ module AEMO
14
16
  attr_accessor :datetime, :region, :total_demand, :rrp, :period_type
15
17
 
16
18
  # Create a new instance of an Interval
17
- #
19
+ #
18
20
  # @param [Time] datetime
19
21
  # @param [Hash] options Hash of optional data values
20
22
  # @return [AEMO::Market::Interval]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AEMO
2
4
  module Market
3
5
  # AEMO::Market::Interval
@@ -6,7 +8,7 @@ module AEMO
6
8
  # @abstract
7
9
  # @since 0.1.0
8
10
  class Node
9
- IDENTIFIERS = %w(NSW QLD SA TAS VIC)
11
+ IDENTIFIERS = %w[NSW QLD SA TAS VIC].freeze
10
12
 
11
13
  attr_accessor :identifier
12
14
 
@@ -31,7 +33,7 @@ module AEMO
31
33
  #
32
34
  # @return [Array<AEMO::Market::Interval>]
33
35
  def current_trading
34
- if @current_trading.empty? || @current_trading.reject { |i| i.period_type != 'TRADE' }.last.datetime != (Time.now - Time.now.to_i % 300)
36
+ if @current_trading.empty? || @current_trading.select { |i| i.period_type == 'TRADE' }.last.datetime != (Time.now - Time.now.to_i % 300)
35
37
  @current_trading = AEMO::Market.current_trading(@identifier)
36
38
  end
37
39
  @current_trading
data/lib/aemo/meter.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AEMO
4
+ # AEMO::Meter represents a meter under a NMI
5
+ class Meter
6
+ attr_accessor :installation_type_code, :next_scheduled_read_date,
7
+ :read_type_code, :registers, :serial_number, :status
8
+
9
+ # Initialize a meter
10
+ #
11
+ # @param [Hash] opts = {} the parameters to set
12
+ # @return [AEMO::Meter] an instance of an AEMO::Meter
13
+ def initialize(opts = {})
14
+ @installation_type_code = opts[:installation_type_code]
15
+ @next_scheduled_read_date = opts[:next_scheduled_read_date]
16
+ @read_type_code = opts[:read_type_code]
17
+ @registers = opts[:registers] || []
18
+ @serial_number = opts[:serial_number]
19
+ @status = opts[:status]
20
+ end
21
+
22
+ # Initialize a new meter from an MSATS hash
23
+ #
24
+ # @param [Hash] meter the MSATS hash
25
+ # @return [AEMO::Meter] description of returned object
26
+ def self.from_hash(meter)
27
+ AEMO::Meter.new(
28
+ installation_type_code: meter['InstallationTypeCode'],
29
+ next_scheduled_read_date: meter['NextScheduledReadDate'],
30
+ read_type_code: meter['ReadTypeCode'],
31
+ registers: [],
32
+ serial_number: meter['SerialNumber'],
33
+ status: meter['Status']
34
+ )
35
+ end
36
+ end
37
+ end
data/lib/aemo/msats.rb CHANGED
@@ -1,8 +1,9 @@
1
- require 'httparty'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'aemo'
3
- # require 'zip'
4
- require 'nokogiri'
5
4
  require 'digest/sha1'
5
+ require 'httparty'
6
+ require 'nokogiri'
6
7
 
7
8
  module AEMO
8
9
  # AEMO::MSATS
@@ -93,7 +94,7 @@ module AEMO
93
94
  # @param [Integer] delivery_point_identifier Delivery Point Identifier
94
95
  # @return [Hash] The response
95
96
  def nmi_discovery_by_delivery_point_identifier(jurisdiction_code, delivery_point_identifier, options = {})
96
- raise ArgumentError, 'jurisdiction_code is not valid' unless %w(ACT NEM NSW QLD SA VIC TAS).include?(jurisdiction_code)
97
+ raise ArgumentError, 'jurisdiction_code is not valid' unless %w[ACT NEM NSW QLD SA VIC TAS].include?(jurisdiction_code)
97
98
  raise ArgumentError, 'delivery_point_identifier is not valid' unless delivery_point_identifier.respond_to?('to_i')
98
99
  raise ArgumentError, 'delivery_point_identifier is not valid' if delivery_point_identifier.to_i < 10_000_000 || delivery_point_identifier.to_i > 99_999_999
99
100
 
@@ -117,7 +118,7 @@ module AEMO
117
118
  # @param [Integer] meter_serial_number The meter's serial number
118
119
  # @return [Hash] The response
119
120
  def nmi_discovery_by_meter_serial_number(jurisdiction_code, meter_serial_number, options = {})
120
- raise ArgumentError, 'jurisdiction_code is not valid' unless %w(ACT NEM NSW QLD SA VIC TAS).include?(jurisdiction_code)
121
+ raise ArgumentError, 'jurisdiction_code is not valid' unless %w[ACT NEM NSW QLD SA VIC TAS].include?(jurisdiction_code)
121
122
 
122
123
  query = {
123
124
  transactionId: transaction_id,
@@ -139,7 +140,7 @@ module AEMO
139
140
  # @param [Integer] meter_serial_number The meter's serial number
140
141
  # @return [Hash] The response
141
142
  def nmi_discovery_by_address(jurisdiction_code, options = {})
142
- raise ArgumentError, 'jurisdiction_code is not valid' unless %w(ACT NEM NSW QLD SA VIC TAS).include?(jurisdiction_code)
143
+ raise ArgumentError, 'jurisdiction_code is not valid' unless %w[ACT NEM NSW QLD SA VIC TAS].include?(jurisdiction_code)
143
144
 
144
145
  options[:building_or_property_name] ||= nil
145
146
  options[:location_descriptor] ||= nil
data/lib/aemo/nem12.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'csv'
2
4
  require 'time'
3
5
 
@@ -92,36 +94,36 @@ module AEMO
92
94
  }.freeze
93
95
 
94
96
  METHOD_FLAGS = {
95
- 11 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Check', description: '' },
96
- 12 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Calculated', description: '' },
97
- 13 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'SCADA', description: '' },
98
- 14 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Like Day', description: '' },
99
- 15 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Average Like Day', description: '' },
100
- 16 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Agreed', description: '' },
101
- 17 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Linear', description: '' },
102
- 18 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Alternate', description: '' },
103
- 19 => { type: %w(SUB), installation_type: [1, 2, 3, 4], short_descriptor: 'Zero', description: '' },
104
- 51 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Previous Year', description: '' },
105
- 52 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Previous Read', description: '' },
106
- 53 => { type: %w(SUB), installation_type: 5, short_descriptor: 'Revision', description: '' },
107
- 54 => { type: %w(SUB), installation_type: 5, short_descriptor: 'Linear', description: '' },
108
- 55 => { type: %w(SUB), installation_type: 5, short_descriptor: 'Agreed', description: '' },
109
- 56 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Prior to First Read - Agreed', description: '' },
110
- 57 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Customer Class', description: '' },
111
- 58 => { type: %w(EST SUB), installation_type: 5, short_descriptor: 'Zero', description: '' },
112
- 61 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Previous Year', description: '' },
113
- 62 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Previous Read', description: '' },
114
- 63 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Customer Class', description: '' },
115
- 64 => { type: %w(SUB), installation_type: 6, short_descriptor: 'Agreed', description: '' },
116
- 65 => { type: %w(EST), installation_type: 6, short_descriptor: 'ADL', description: '' },
117
- 66 => { type: %w(SUB), installation_type: 6, short_descriptor: 'Revision', description: '' },
118
- 67 => { type: %w(SUB), installation_type: 6, short_descriptor: 'Customer Read', description: '' },
119
- 68 => { type: %w(EST SUB), installation_type: 6, short_descriptor: 'Zero', description: '' },
120
- 71 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Recalculation', description: '' },
121
- 72 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Revised Table', description: '' },
122
- 73 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Revised Algorithm', description: '' },
123
- 74 => { type: %w(SUB), installation_type: 7, short_descriptor: 'Agreed', description: '' },
124
- 75 => { type: %w(EST), installation_type: 7, short_descriptor: 'Existing Table', description: '' }
97
+ 11 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Check', description: '' },
98
+ 12 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Calculated', description: '' },
99
+ 13 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'SCADA', description: '' },
100
+ 14 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Like Day', description: '' },
101
+ 15 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Average Like Day', description: '' },
102
+ 16 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Agreed', description: '' },
103
+ 17 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Linear', description: '' },
104
+ 18 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Alternate', description: '' },
105
+ 19 => { type: %w[SUB], installation_type: [1, 2, 3, 4], short_descriptor: 'Zero', description: '' },
106
+ 51 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Previous Year', description: '' },
107
+ 52 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Previous Read', description: '' },
108
+ 53 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Revision', description: '' },
109
+ 54 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Linear', description: '' },
110
+ 55 => { type: %w[SUB], installation_type: 5, short_descriptor: 'Agreed', description: '' },
111
+ 56 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Prior to First Read - Agreed', description: '' },
112
+ 57 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Customer Class', description: '' },
113
+ 58 => { type: %w[EST SUB], installation_type: 5, short_descriptor: 'Zero', description: '' },
114
+ 61 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Previous Year', description: '' },
115
+ 62 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Previous Read', description: '' },
116
+ 63 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Customer Class', description: '' },
117
+ 64 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Agreed', description: '' },
118
+ 65 => { type: %w[EST], installation_type: 6, short_descriptor: 'ADL', description: '' },
119
+ 66 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Revision', description: '' },
120
+ 67 => { type: %w[SUB], installation_type: 6, short_descriptor: 'Customer Read', description: '' },
121
+ 68 => { type: %w[EST SUB], installation_type: 6, short_descriptor: 'Zero', description: '' },
122
+ 71 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Recalculation', description: '' },
123
+ 72 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Revised Table', description: '' },
124
+ 73 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Revised Algorithm', description: '' },
125
+ 74 => { type: %w[SUB], installation_type: 7, short_descriptor: 'Agreed', description: '' },
126
+ 75 => { type: %w[EST], installation_type: 7, short_descriptor: 'Existing Table', description: '' }
125
127
  }.freeze
126
128
 
127
129
  REASON_CODES = {
@@ -332,7 +334,7 @@ module AEMO
332
334
  end
333
335
  raise ArgumentError, 'UOM is not valid' if csv[7].upcase.match(/[A-Z0-9]{2}/).nil?
334
336
  raise ArgumentError, 'UOM is not valid' unless UOM.keys.map(&:upcase).include?(csv[7].upcase)
335
- raise ArgumentError, 'IntervalLength is not valid' unless %w(1 5 10 15 30).include?(csv[8])
337
+ raise ArgumentError, 'IntervalLength is not valid' unless %w[1 5 10 15 30].include?(csv[8])
336
338
  # raise ArgumentError, 'NextScheduledReadDate is not valid' if csv[9].match(/\d{8}/).nil? || csv[9] != Time.parse('#{csv[9]}').strftime('%Y%m%d')
337
339
 
338
340
  @nmi = AEMO::NMI.new(csv[1])
@@ -369,14 +371,14 @@ module AEMO
369
371
  raise ArgumentError, 'QualityMethod is not valid' unless csv[intervals_offset + 0].class == String
370
372
  raise ArgumentError, 'QualityMethod does not have valid length' unless [1, 3].include?(csv[intervals_offset + 0].length)
371
373
  raise ArgumentError, 'QualityMethod does not have valid QualityFlag' unless QUALITY_FLAGS.keys.include?(csv[intervals_offset + 0][0])
372
- unless %w(A N V).include?(csv[intervals_offset + 0][0])
374
+ unless %w[A N V].include?(csv[intervals_offset + 0][0])
373
375
  raise ArgumentError, 'QualityMethod does not have valid length' unless csv[intervals_offset + 0].length == 3
374
376
  raise ArgumentError, 'QualityMethod does not have valid MethodFlag' unless METHOD_FLAGS.keys.include?(csv[intervals_offset + 0][1..2].to_i)
375
377
  end
376
- unless %w(A N E).include?(csv[intervals_offset + 0][0])
378
+ unless %w[A N E].include?(csv[intervals_offset + 0][0])
377
379
  raise ArgumentError, 'ReasonCode is not valid' unless REASON_CODES.keys.include?(csv[intervals_offset + 1].to_i)
378
380
  end
379
- if !csv[intervals_offset + 1].nil? && csv[intervals_offset + 1].to_i == 0
381
+ if !csv[intervals_offset + 1].nil? && csv[intervals_offset + 1].to_i.zero?
380
382
  raise ArgumentError, 'ReasonDescription is not valid' unless csv[intervals_offset + 2].class == String && !csv[intervals_offset + 2].empty?
381
383
  end
382
384
  if options[:strict]
@@ -429,7 +431,7 @@ module AEMO
429
431
  interval_events = []
430
432
 
431
433
  # Only need to update flags for EFSV
432
- unless %w(A N).include?csv[3]
434
+ unless %w[A N].include? csv[3]
433
435
  number_of_intervals = 1440 / @data_details.last[:interval_length]
434
436
  interval_start_point = @interval_data.length - number_of_intervals
435
437
 
@@ -460,13 +462,11 @@ module AEMO
460
462
 
461
463
  # @param [String] line A single line in string format
462
464
  # @return [Hash] the line parsed into a hash of information
463
- def parse_nem12_500(_line, _options = {})
464
- end
465
+ def parse_nem12_500(_line, _options = {}); end
465
466
 
466
467
  # @param [String] line A single line in string format
467
468
  # @return [Hash] the line parsed into a hash of information
468
- def parse_nem12_900(_line, _options = {})
469
- end
469
+ def parse_nem12_900(_line, _options = {}); end
470
470
 
471
471
  # Turns the flag to a string
472
472
  #
@@ -498,7 +498,7 @@ module AEMO
498
498
 
499
499
  # @return [Array] CSV of a NEM12 file a given Meter + Data Stream for easy reading
500
500
  def to_csv
501
- headers = %w(nmi suffix units datetime value flags)
501
+ headers = %w[nmi suffix units datetime value flags]
502
502
  ([headers] + to_a.map do |row|
503
503
  row[3] = row[3].strftime('%Y%m%d%H%M%S%z')
504
504
  row
data/lib/aemo/nem13.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AEMO
2
4
  class NEM13
3
5
  end
data/lib/aemo/nmi.rb CHANGED
@@ -1,412 +1,328 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'csv'
2
4
  require 'json'
3
5
  require 'time'
4
6
  require 'ostruct'
5
7
  module AEMO
6
- # AEMO::NMI acts as an object to simplify access to data and information about a NMI and provide verification of the NMI value
8
+ # AEMO::NMI acts as an object to simplify access to data and information
9
+ # about a NMI and provide verification of the NMI value
7
10
  class NMI
8
11
  # Operational Regions for the NMI
9
- REGIONS = {
10
- 'ACT' => 'Australian Capital Territory',
11
- 'NSW' => 'New South Wales',
12
- 'QLD' => 'Queensland',
13
- 'SA' => 'South Australia',
14
- 'TAS' => 'Tasmania',
15
- 'VIC' => 'Victoria',
16
- 'WA' => 'Western Australia',
17
- 'NT' => 'Northern Territory'
18
- }.freeze
12
+ REGIONS = { 'ACT' => 'Australian Capital Territory',
13
+ 'NSW' => 'New South Wales',
14
+ 'QLD' => 'Queensland',
15
+ 'SA' => 'South Australia',
16
+ 'TAS' => 'Tasmania',
17
+ 'VIC' => 'Victoria',
18
+ 'WA' => 'Western Australia',
19
+ 'NT' => 'Northern Territory' }.freeze
19
20
 
20
- # NMI_ALLOCATIONS as per AEMO Documentation at http://aemo.com.au/Electricity/Policies-and-Procedures/Retail-and-Metering/~/media/Files/Other/Retail% 20and% 20Metering/NMI_Allocation_List_v7_June_2012.ashx
21
- # Last accessed 2016-05-15
21
+ # NMI_ALLOCATIONS as per AEMO Documentation at
22
+ # https://www.aemo.com.au/-/media/Files/Electricity/NEM/Retail_and_Metering/
23
+ # Metering-Procedures/NMI-Allocation-List.pdf
24
+ # Last accessed 2017-08-01
22
25
  NMI_ALLOCATIONS = {
23
26
  'ACTEWP' => {
24
27
  title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
25
28
  friendly_title: 'ACTEWAgl',
26
29
  state: AEMO::Region.new('ACT'),
27
30
  type: 'electricity',
28
- includes: [
29
- /^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
30
- /^(7001\d{6})$/
31
- ],
32
- excludes: [
33
- ]
31
+ includes: [/^(NGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
32
+ /^(7001\d{6})$/],
33
+ excludes: []
34
34
  },
35
35
  'CNRGYP' => {
36
36
  title: 'Essential Energy',
37
37
  friendly_title: 'Essential Energy',
38
38
  state: AEMO::Region.new('NSW'),
39
39
  type: 'electricity',
40
- includes: [
41
- /^(NAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
42
- /^(NBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
43
- /^(NDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
44
- /^(NFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
45
- /^(4001\d{6})$/,
46
- /^(4508\d{6})$/,
47
- /^(4204\d{6})$/,
48
- /^(4407\d{6})$/
49
- ],
50
- excludes: [
51
- ]
40
+ includes: [/^(NAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
41
+ /^(NBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
42
+ /^(NDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
43
+ /^(NFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
44
+ /^(4001\d{6})$/,
45
+ /^(4508\d{6})$/,
46
+ /^(4204\d{6})$/,
47
+ /^(4407\d{6})$/],
48
+ excludes: []
52
49
  },
53
50
  'ENERGYAP' => {
54
51
  title: 'Ausgrid',
55
52
  friendly_title: 'Ausgrid',
56
53
  state: AEMO::Region.new('NSW'),
57
54
  type: 'electricity',
58
- includes: [
59
- /^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
60
- /^(410[234]\d{6})$/
61
- ],
62
- excludes: [
63
- ]
55
+ includes: [/^(NCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
56
+ /^(410[234]\d{6})$/],
57
+ excludes: []
64
58
  },
65
59
  'INTEGP' => {
66
60
  title: 'Endeavour Energy',
67
61
  friendly_title: 'Endeavour Energy',
68
62
  state: AEMO::Region.new('NSW'),
69
63
  type: 'electricity',
70
- includes: [
71
- /^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
72
- /^(431\d{7})$/
73
- ],
74
- excludes: [
75
- ]
64
+ includes: [/^(NEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
65
+ /^(431\d{7})$/],
66
+ excludes: []
76
67
  },
77
68
  'TRANSGP' => {
78
69
  title: 'TransGrid',
79
70
  friendly_title: 'TransGrid',
80
71
  state: AEMO::Region.new('NSW'),
81
72
  type: 'electricity',
82
- includes: [
83
- /^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
84
- /^(460810[0-8]\d{3})$/
85
- ],
86
- excludes: [
87
- ]
73
+ includes: [/^(NTTT[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
74
+ /^(460810[0-8]\d{3})$/],
75
+ excludes: []
88
76
  },
89
77
  'SNOWY' => {
90
78
  title: 'Snowy Hydro Ltd',
91
79
  friendly_title: 'Snowy Hydro',
92
80
  state: AEMO::Region.new('NSW'),
93
81
  type: 'electricity',
94
- includes: [
95
- /^(4708109\d{3})$/
96
- ],
97
- excludes: [
98
- ]
82
+ includes: [/^(4708109\d{3})$/],
83
+ excludes: []
99
84
  },
100
85
  'NT_RESERVED' => {
101
86
  title: 'Northern Territory Reserved Block',
102
87
  friendly_title: 'Northern Territory Reserved Block',
103
88
  state: AEMO::Region.new('NT'),
104
89
  type: 'electricity',
105
- includes: [
106
- /^(250\d{7})$/
107
- ],
108
- excludes: [
109
- ]
90
+ includes: [/^(250\d{7})$/],
91
+ excludes: []
110
92
  },
111
93
  'ERGONETP' => {
112
94
  title: 'Ergon Energy Corporation',
113
95
  friendly_title: 'Ergon Energy',
114
96
  state: AEMO::Region.new('QLD'),
115
97
  type: 'electricity',
116
- includes: [
117
- /^(QAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
118
- /^(QCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
119
- /^(QDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
120
- /^(QEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
121
- /^(QFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
122
- /^(QGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
123
- /^(30\d{8})$/
124
- ],
125
- excludes: [
126
- ]
98
+ includes: [/^(QAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
99
+ /^(QCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
100
+ /^(QDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
101
+ /^(QEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
102
+ /^(QFFF[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
103
+ /^(QGGG[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
104
+ /^(30\d{8})$/],
105
+ excludes: []
127
106
  },
128
107
  'ENERGEXP' => {
129
108
  title: 'ENERGEX Limited',
130
109
  friendly_title: 'Energex',
131
110
  state: AEMO::Region.new('QLD'),
132
111
  type: 'electricity',
133
- includes: [
134
- /^(QB\d{2}[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
135
- /^(31\d{8})$/
136
- ],
137
- excludes: [
138
- ]
112
+ includes: [/^(QB\d{2}[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
113
+ /^(31\d{8})$/],
114
+ excludes: []
139
115
  },
140
116
  'PLINKP' => {
141
117
  title: 'Qld Electricity Transmission Corp (Powerlink)',
142
118
  friendly_title: 'Powerlink',
143
119
  state: AEMO::Region.new('QLD'),
144
120
  type: 'electricity',
145
- includes: [
146
- /^(Q[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
147
- /^(320200\d{4})$/
148
- ],
149
- excludes: [
150
- ]
121
+ includes: [/^(Q[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
122
+ /^(320200\d{4})$/],
123
+ excludes: []
151
124
  },
152
125
  'UMPLP' => {
153
126
  title: 'SA Power Networks',
154
127
  friendly_title: 'SA Power Networks',
155
128
  state: AEMO::Region.new('SA'),
156
129
  type: 'electricity',
157
- includes: [
158
- /^(SAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
159
- /^(SASMPL[\d]{4})$/,
160
- /^(200[12]\d{6})$/
161
- ],
162
- excludes: [
163
- ]
130
+ includes: [/^(SAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
131
+ /^(SASMPL[\d]{4})$/,
132
+ /^(200[12]\d{6})$/],
133
+ excludes: []
164
134
  },
165
135
  'ETSATP' => {
166
136
  title: 'ElectraNet SA',
167
137
  friendly_title: 'ElectraNet SA',
168
138
  state: AEMO::Region.new('SA'),
169
139
  type: 'electricity',
170
- includes: [
171
- /^(S[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
172
- /^(210200\d{4})$/
173
- ],
174
- excludes: [
175
- ]
140
+ includes: [/^(S[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
141
+ /^(210200\d{4})$/],
142
+ excludes: []
176
143
  },
177
144
  'AURORAP' => {
178
145
  title: 'TasNetworks',
179
146
  friendly_title: 'TasNetworks',
180
147
  state: AEMO::Region.new('TAS'),
181
148
  type: 'electricity',
182
- includes: [
183
- /^(T000000(([0-4]\d{3})|(500[01])))$/,
184
- /^(8000\d{6})$/,
185
- /^(8590[23]\d{5})$/
186
- ],
187
- excludes: [
188
- ]
149
+ includes: [/^(T000000(([0-4]\d{3})|(500[01])))$/,
150
+ /^(8000\d{6})$/,
151
+ /^(8590[23]\d{5})$/],
152
+ excludes: []
189
153
  },
190
154
  'TRANSEND' => {
191
155
  title: 'TasNetworks',
192
156
  friendly_title: 'TasNetworks',
193
157
  state: AEMO::Region.new('TAS'),
194
158
  type: 'electricity',
195
- includes: [
196
- /^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/
197
- ],
198
- excludes: [
199
- ]
159
+ includes: [/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/],
160
+ excludes: []
200
161
  },
201
162
  'CITIPP' => {
202
163
  title: 'CitiPower',
203
164
  friendly_title: 'CitiPower',
204
165
  state: AEMO::Region.new('VIC'),
205
166
  type: 'electricity',
206
- includes: [
207
- /^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
208
- /^(610[23]\d{6})$/
209
- ],
210
- excludes: [
211
- ]
167
+ includes: [/^(VAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
168
+ /^(610[23]\d{6})$/],
169
+ excludes: []
212
170
  },
213
171
  'EASTERN' => {
214
172
  title: 'SP AusNet',
215
173
  friendly_title: 'SP AusNet DNSP',
216
174
  state: AEMO::Region.new('VIC'),
217
175
  type: 'electricity',
218
- includes: [
219
- /^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
220
- /^(630[56]\d{6})$/
221
- ],
222
- excludes: [
223
- ]
176
+ includes: [/^(VBBB[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
177
+ /^(630[56]\d{6})$/],
178
+ excludes: []
224
179
  },
225
180
  'POWCP' => {
226
181
  title: 'PowerCor Australia',
227
182
  friendly_title: 'PowerCor',
228
183
  state: AEMO::Region.new('VIC'),
229
184
  type: 'electricity',
230
- includes: [
231
- /^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
232
- /^(620[34]\d{6})$/
233
- ],
234
- excludes: [
235
- ]
185
+ includes: [/^(VCCC[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
186
+ /^(620[34]\d{6})$/],
187
+ excludes: []
236
188
  },
237
189
  'SOLARISP' => {
238
190
  title: 'Jemena Electricity Networks (VIC)',
239
191
  friendly_title: 'Jemena',
240
192
  state: AEMO::Region.new('VIC'),
241
193
  type: 'electricity',
242
- includes: [
243
- /^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
244
- /^(6001\d{6})$/
245
- ],
246
- excludes: [
247
- ]
194
+ includes: [/^(VDDD[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
195
+ /^(6001\d{6})$/],
196
+ excludes: []
248
197
  },
249
198
  'UNITED' => {
250
199
  title: 'United Energy Distribution',
251
200
  friendly_title: 'United Energy',
252
201
  state: AEMO::Region.new('VIC'),
253
202
  type: 'electricity',
254
- includes: [
255
- /^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
256
- /^(640[78]\d{6})$/
257
- ],
258
- excludes: [
259
- ]
203
+ includes: [/^(VEEE[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
204
+ /^(640[78]\d{6})$/],
205
+ excludes: []
260
206
  },
261
207
  'GPUPP' => {
262
208
  title: 'SP AusNet TNSP',
263
209
  friendly_title: 'SP AusNet TNSP',
264
210
  state: AEMO::Region.new('VIC'),
265
211
  type: 'electricity',
266
- includes: [
267
- /^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
268
- /^(650900\d{4})$/
269
- ],
270
- excludes: [
271
- ]
212
+ includes: [/^(V[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/,
213
+ /^(650900\d{4})$/],
214
+ excludes: []
272
215
  },
273
216
  'WESTERNPOWER' => {
274
217
  title: 'Western Power',
275
218
  friendly_title: 'Western Power',
276
219
  state: AEMO::Region.new('WA'),
277
220
  type: 'electricity',
278
- includes: [
279
- /^(WAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
280
- /^(800[1-9]\d{6})$/,
281
- /^(801\d{7})$/,
282
- /^(8020\d{6})$/
283
- ],
284
- excludes: [
285
- ]
221
+ includes: [/^(WAAA[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
222
+ /^(800[1-9]\d{6})$/,
223
+ /^(801\d{7})$/,
224
+ /^(8020\d{6})$/],
225
+ excludes: []
286
226
  },
287
227
  'HORIZONPOWER' => {
288
228
  title: 'Horizon Power',
289
229
  friendly_title: 'Horizon Power',
290
230
  state: AEMO::Region.new('WA'),
291
231
  type: 'electricity',
292
- includes: [
293
- /^(8021\d{6})$/
294
- ],
295
- excludes: [
296
- ]
232
+ includes: [/^(8021\d{6})$/],
233
+ excludes: []
297
234
  },
298
235
  'GAS_NSW' => {
299
236
  title: 'GAS NSW',
300
237
  friendly_title: 'GAS NSW',
301
238
  state: AEMO::Region.new('NSW'),
302
239
  type: 'gas',
303
- includes: [
304
- /^(52\d{8})$/
305
- ],
306
- excludes: [
307
- ]
240
+ includes: [/^(52\d{8})$/],
241
+ excludes: []
308
242
  },
309
243
  'GAS_VIC' => {
310
244
  title: 'GAS VIC',
311
245
  friendly_title: 'GAS VIC',
312
246
  state: AEMO::Region.new('VIC'),
313
247
  type: 'gas',
314
- includes: [
315
- /^(53\d{8})$/
316
- ],
317
- excludes: [
318
- ]
248
+ includes: [/^(53\d{8})$/],
249
+ excludes: []
319
250
  },
320
251
  'GAS_QLD' => {
321
252
  title: 'GAS QLD',
322
253
  friendly_title: 'GAS QLD',
323
254
  state: AEMO::Region.new('QLD'),
324
255
  type: 'gas',
325
- includes: [
326
- /^(54\d{8})$/
327
- ],
328
- excludes: [
329
- ]
256
+ includes: [/^(54\d{8})$/],
257
+ excludes: []
330
258
  },
331
259
  'GAS_SA' => {
332
260
  title: 'GAS SA',
333
261
  friendly_title: 'GAS SA',
334
262
  state: AEMO::Region.new('SA'),
335
263
  type: 'gas',
336
- includes: [
337
- /^(55\d{8})$/
338
- ],
339
- excludes: [
340
- ]
264
+ includes: [/^(55\d{8})$/],
265
+ excludes: []
341
266
  },
342
267
  'GAS_WA' => {
343
268
  title: 'GAS WA',
344
269
  friendly_title: 'GAS WA',
345
270
  state: AEMO::Region.new('WA'),
346
271
  type: 'gas',
347
- includes: [
348
- /^(56\d{8})$/
349
- ],
350
- excludes: [
351
- ]
272
+ includes: [/^(56\d{8})$/],
273
+ excludes: []
352
274
  },
353
275
  'GAS_TAS' => {
354
276
  title: 'GAS TAS',
355
277
  friendly_title: 'GAS TAS',
356
278
  state: AEMO::Region.new('TAS'),
357
279
  type: 'gas',
358
- includes: [
359
- /^(57\d{8})$/
360
- ],
361
- excludes: [
362
- ]
280
+ includes: [/^(57\d{8})$/],
281
+ excludes: []
363
282
  },
364
283
  'FEDAIRPORTS' => {
365
284
  title: 'Federal Airports Corporation (Sydney Airport)',
366
285
  friendly_title: 'Sydney Airport',
367
286
  state: AEMO::Region.new('NSW'),
368
287
  type: 'electricity',
369
- includes: [
370
- /^(NJJJNR[A-HJ-NP-Z\d]{4})$/
371
- ],
372
- excludes: [
373
- ]
288
+ includes: [/^(NJJJNR[A-HJ-NP-Z\d]{4})$/],
289
+ excludes: []
374
290
  },
375
291
  'EXEMPTNETWORKS' => {
376
292
  title: 'Exempt Networks - various',
377
293
  friendly_title: 'Exempt Networks - various',
378
294
  state: '',
379
295
  type: 'electricity',
380
- includes: [
381
- /^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
382
- /^(7102\d{6})$/
383
- ],
384
- excludes: [
385
- ]
296
+ includes: [/^(NKKK[A-HJ-NP-VX-Z\d][A-HJ-NP-Z\d]{5})$/,
297
+ /^(7102\d{6})$/],
298
+ excludes: []
386
299
  },
387
300
  'AEMORESERVED' => {
388
301
  title: 'AEMO Reserved',
389
302
  friendly_title: 'AEMO Reserved',
390
303
  state: '',
391
304
  type: 'electricity',
392
- includes: [
393
- /^(880[1-5]\d{6})$/,
394
- /^(9\d{9})$/
395
- ],
396
- excludes: [
397
- ]
305
+ includes: [/^(880[1-5]\d{6})$/,
306
+ /^(9\d{9})$/],
307
+ excludes: []
398
308
  }
399
309
  }.freeze
400
310
  # Transmission Node Identifier Codes are loaded from a json file
401
311
  # Obtained from http://www.nemweb.com.au/
402
312
  #
403
313
  # See /lib/data for further data manipulation required
404
- TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..', 'data', 'aemo-tni.json'))).freeze
314
+ TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..',
315
+ 'data', 'aemo-tni.json'))).freeze
405
316
  # Distribution Loss Factor Codes are loaded from a json file
406
- # Obtained from MSATS, matching to DNSP from file http://www.aemo.com.au/Electricity/Market-Operations/Loss-Factors-and-Regional-Boundaries/~/media/Files/Other/loss% 20factors/DLF_FINAL_V2_2014_2015.ashx
407
- # Last accessed 2015-02-06
317
+ # Obtained from MSATS, matching to DNSP from file
318
+ # https://www.aemo.com.au/-/media/Files/Electricity/NEM/
319
+ # Security_and_Reliability/Loss_Factors_and_Regional_Boundaries/
320
+ # 2016/DLF_V3_2016_2017.pdf
321
+ #
322
+ # Last accessed 2017-08-01
408
323
  # See /lib/data for further data manipulation required
409
- DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..', 'data', 'aemo-dlf.json'))).freeze
324
+ DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..',
325
+ 'data', 'aemo-dlf.json'))).freeze
410
326
 
411
327
  # [String] National Meter Identifier
412
328
  @nmi = nil
@@ -423,18 +339,24 @@ module AEMO
423
339
  @roles = nil
424
340
  @data_streams = nil
425
341
 
426
- attr_accessor :nmi, :msats_detail, :tni, :dlf, :customer_classification_code, :customer_threshold_code, :jurisdiction_code, :classification_code, :status, :address, :meters, :roles, :data_streams
342
+ attr_accessor :nmi, :msats_detail, :tni, :dlf,
343
+ :customer_classification_code, :customer_threshold_code,
344
+ :jurisdiction_code, :classification_code, :status, :address,
345
+ :meters, :roles, :data_streams
427
346
 
428
347
  # Initialize a NMI file
429
348
  #
430
349
  # @param [String] nmi the National Meter Identifier (NMI)
431
350
  # @param [Hash] options a hash of options
432
- # @option options [Hash] :msats_detail MSATS details as per #parse_msats_detail requirements
351
+ # @option options [Hash] :msats_detail MSATS details as per
352
+ # #parse_msats_detail requirements
433
353
  # @return [AEMO::NMI] an instance of AEMO::NMI is returned
434
354
  def initialize(nmi, options = {})
435
355
  raise ArgumentError, 'NMI is not a string' unless nmi.is_a?(String)
436
356
  raise ArgumentError, 'NMI is not 10 characters' unless nmi.length == 10
437
- raise ArgumentError, 'NMI is not constructed with valid characters' unless AEMO::NMI.valid_nmi?(nmi)
357
+ unless AEMO::NMI.valid_nmi?(nmi)
358
+ raise ArgumentError, 'NMI is not constructed with valid characters'
359
+ end
438
360
 
439
361
  @nmi = nmi
440
362
  @meters = []
@@ -459,17 +381,21 @@ module AEMO
459
381
  AEMO::NMI.network(@nmi)
460
382
  end
461
383
 
462
- # A function to calculate the checksum value for a given National Meter Identifier
384
+ # A function to calculate the checksum value for a given
385
+ # National Meter Identifier
463
386
  #
464
- # @param [Integer] checksum_value the checksum value to check against the current National Meter Identifier's checksum value
387
+ # @param [Integer] checksum_value the checksum value to check against the
388
+ # current National Meter Identifier's checksum value
465
389
  # @return [Boolean] whether or not the checksum is valid
466
390
  def valid_checksum?(checksum_value)
467
391
  checksum_value == checksum
468
392
  end
469
393
 
470
- # Checksum is a function to calculate the checksum value for a given National Meter Identifier
394
+ # Checksum is a function to calculate the checksum value for a given
395
+ # National Meter Identifier
471
396
  #
472
- # @return [Integer] the checksum value for the current National Meter Identifier
397
+ # @return [Integer] the checksum value for the current National Meter
398
+ # Identifier
473
399
  def checksum
474
400
  summation = 0
475
401
  @nmi.reverse.split(//).each_index do |i|
@@ -486,12 +412,14 @@ module AEMO
486
412
  #
487
413
  # @return [Hash] MSATS NMI Detail data
488
414
  def raw_msats_nmi_detail(options = {})
489
- raise ArgumentError,
490
- 'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate?
415
+ unless AEMO::MSATS.can_authenticate?
416
+ raise ArgumentError, 'MSATS has no authentication credentials'
417
+ end
491
418
  AEMO::MSATS.nmi_detail(@nmi, options)
492
419
  end
493
420
 
494
- # Provided MSATS is configured, uses the raw MSATS data to augment NMI information
421
+ # Provided MSATS is configured, uses the raw MSATS data to augment NMI
422
+ # information
495
423
  #
496
424
  # @return [self] returns self
497
425
  def update_from_msats!(options = {})
@@ -523,27 +451,13 @@ module AEMO
523
451
  unless @msats_detail['MeterRegister'].nil?
524
452
  meters = @msats_detail['MeterRegister']['Meter']
525
453
  meters = [meters] if meters.is_a?(Hash)
526
- meters.select { |x| !x['Status'].nil? }.each do |meter|
527
- @meters << OpenStruct.new(
528
- status: meter['Status'],
529
- installation_type_code: meter['InstallationTypeCode'],
530
- next_scheduled_read_date: meter['NextScheduledReadDate'],
531
- read_type_code: meter['ReadTypeCode'],
532
- registers: [],
533
- serial_number: meter['SerialNumber']
534
- )
454
+ meters.reject { |x| x['Status'].nil? }.each do |meter|
455
+ @meters << AEMO::Meter.from_hash(meter)
535
456
  end
536
457
  meters.select { |x| x['Status'].nil? }.each do |registers|
537
458
  m = @meters.find { |x| x.serial_number == registers['SerialNumber'] }
538
- m.registers << OpenStruct.new(
539
- controlled_load: (registers['RegisterConfiguration']['Register']['ControlledLoad'] == 'Y'),
540
- dial_format: registers['RegisterConfiguration']['Register']['DialFormat'],
541
- multiplier: registers['RegisterConfiguration']['Register']['Multiplier'],
542
- network_tariff_code: registers['RegisterConfiguration']['Register']['NetworkTariffCode'],
543
- register_id: registers['RegisterConfiguration']['Register']['RegisterID'],
544
- status: registers['RegisterConfiguration']['Register']['Status'],
545
- time_of_day: registers['RegisterConfiguration']['Register']['TimeOfDay'],
546
- unit_of_measure: registers['RegisterConfiguration']['Register']['UnitOfMeasure']
459
+ m.registers << AEMO::Register.from_hash(
460
+ registers['RegisterConfiguration']['Register']
547
461
  )
548
462
  end
549
463
  end
@@ -588,10 +502,11 @@ module AEMO
588
502
  friendly_address
589
503
  end
590
504
 
591
- # Returns the meter OpenStructs for the requested status (C/R)
505
+ # Returns the meters for the requested status (C/R)
592
506
  #
593
507
  # @param [String] status the stateus [C|R]
594
- # @return [Array<OpenStruct>] Returns an array of OpenStructs for Meters with the status provided
508
+ # @return [Array<AEMO::Meter>] Returns an array of AEMO::Meters with the
509
+ # status provided
595
510
  def meters_by_status(status = 'C')
596
511
  @meters.select { |x| x.status == status.to_s }
597
512
  end
@@ -599,7 +514,8 @@ module AEMO
599
514
  # Returns the data_stream OpenStructs for the requested status (A/I)
600
515
  #
601
516
  # @param [String] status the stateus [A|I]
602
- # @return [Array<OpenStruct>] Returns an array of OpenStructs for the current Meters
517
+ # @return [Array<OpenStruct>] Returns an array of OpenStructs for the
518
+ # current Meters
603
519
  def data_streams_by_status(status = 'A')
604
520
  @data_streams.select { |x| x.status == status.to_s }
605
521
  end
@@ -608,7 +524,8 @@ module AEMO
608
524
  #
609
525
  # @return [Integer] the current daily load for the meter in kWh
610
526
  def current_daily_load
611
- data_streams_by_status.map { |x| x.averaged_daily_load.to_i }.inject(0, :+)
527
+ data_streams_by_status.map { |x| x.averaged_daily_load.to_i }
528
+ .inject(0, :+)
612
529
  end
613
530
 
614
531
  # The current annual load in MWh
@@ -626,10 +543,12 @@ module AEMO
626
543
  ((nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?)
627
544
  end
628
545
 
629
- # A function to calculate the checksum value for a given National Meter Identifier
546
+ # A function to calculate the checksum value for a given National Meter
547
+ # Identifier
630
548
  #
631
549
  # @param [String] nmi the NMI to check the checksum against
632
- # @param [Integer] checksum_value the checksum value to check against the current National Meter Identifier's checksum value
550
+ # @param [Integer] checksum_value the checksum value to check against the
551
+ # current National Meter Identifier's checksum value
633
552
  # @return [Boolean] whether or not the checksum is valid
634
553
  def self.valid_checksum?(nmi, checksum_value)
635
554
  nmi = AEMO::NMI.new(nmi)
@@ -655,13 +574,20 @@ module AEMO
655
574
 
656
575
  # A function to return the distribution loss factor value for a given date
657
576
  #
658
- # @param [DateTime, Time] datetime the date for the distribution loss factor value
577
+ # @param [DateTime, Time] datetime the date for the distribution loss factor
578
+ # value
659
579
  # @return [nil, float] the distribution loss factor value
660
580
  def dlfc_value(datetime = DateTime.now)
661
- raise 'No DLF set, ensure that you have set the value either via the update_from_msats! function or manually' if @dlf.nil?
581
+ if @dlf.nil?
582
+ raise 'No DLF set, ensure that you have set the value either via the' \
583
+ 'update_from_msats! function or manually'
584
+ end
662
585
  raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
663
586
  raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
664
- possible_values = DLF_CODES[@dlf].select { |x| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
587
+ possible_values = DLF_CODES[@dlf].select do |x|
588
+ DateTime.parse(x['FromDate']) <= datetime &&
589
+ DateTime.parse(x['ToDate']) >= datetime
590
+ end
665
591
  if possible_values.empty?
666
592
  nil
667
593
  else