forked from jeffkowalski/sunpower
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sunpower.rb
executable file
·130 lines (111 loc) · 3.85 KB
/
sunpower.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'thor'
require 'fileutils'
require 'logger'
require 'rest-client'
require 'json'
require 'yaml'
require 'influxdb'
LOGFILE = File.join(Dir.home, '.log', 'sunpower.log')
CREDENTIALS_PATH = File.join(Dir.home, '.credentials', 'sunpower.yaml')
API_BASE_URL = 'https://elhapi.edp.sunpower.com/v1/elh'
module Kernel
def with_rescue(exceptions, logger, retries: 5)
try = 0
begin
yield try
rescue *exceptions => e
try += 1
raise if try > retries
logger.info "caught error #{e.class}, retrying (#{try}/#{retries})..."
retry
end
end
end
class String
def numeric?
!Float(self).nil?
rescue StandardError
false
end
end
class Sunpower < Thor
no_commands do
def redirect_output
unless LOGFILE == 'STDOUT'
logfile = File.expand_path(LOGFILE)
FileUtils.mkdir_p(File.dirname(logfile), mode: 0o755)
FileUtils.touch logfile
File.chmod 0o644, logfile
$stdout.reopen logfile, 'a'
end
$stderr.reopen $stdout
$stdout.sync = $stderr.sync = true
end
def setup_logger
redirect_output if options[:log]
@logger = Logger.new STDOUT
@logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
@logger.info 'starting'
end
end
no_commands do
def authorize
sunpower_credentials = YAML.load_file CREDENTIALS_PATH
response = RestClient.post "#{API_BASE_URL}/authenticate",
sunpower_credentials.to_json,
'Content-Type' => 'application/json'
authorization = JSON.parse response
@logger.debug authorization
authorization
end
def get_current_power(authorization)
response = with_rescue([RestClient::Unauthorized], @logger) do |_try|
RestClient.get "#{API_BASE_URL}/address/#{authorization['addressId']}/power",
Authorization: "SP-CUSTOM #{authorization['tokenID']}",
params: { async: false }
end
@logger.debug response.headers
@logger.info response
power = JSON.parse response
# TODO: find the actual date of the reading
# this hack below is certainly incorrect
power['Date'] = response.headers[:date]
power
end
end
class_option :log, type: :boolean, default: true, desc: "log output to #{LOGFILE}"
class_option :verbose, type: :boolean, aliases: '-v', desc: 'increase verbosity'
desc 'describe-status', 'describe the current state of the solar panel array'
def describe_status
setup_logger
authorization = authorize
power = get_current_power authorization
puts "#{power['CurrentProduction']}kW at #{power['Date']}"
# hourly_energy_data = RestClient.get "#{API_BASE_URL}/SystemInfo/SystemInfo.svc/getHourlyEnergyData?tokenid=#{tokenid}×tamp=#{TIMESTAMP}"
# energy_data = csv_to_hash_table hourly_energy_data
# lifetime_energy = energy_data.map { |_date, values| values[:ep] }.reduce(0, :+)
# puts "Lifetime energy = #{lifetime_energy} kWh"
end
desc 'record-status', 'record the current solar production to database'
method_option :dry_run, type: :boolean, aliases: '-d', desc: 'do not write to database'
def record_status
setup_logger
begin
power = with_rescue([RestClient::GatewayTimeout, RestClient::Exceptions::OpenTimeout, RestClient::InternalServerError], @logger, retries: 6) do |_try|
authorization = authorize
get_current_power authorization
end
influxdb = InfluxDB::Client.new 'sunpower'
data = {
values: { value: power['CurrentProduction'].to_f },
timestamp: (Time.parse power['Date']).to_i
}
influxdb.write_point('production', data) unless options[:dry_run]
rescue StandardError => e
@logger.error e
end
end
end
Sunpower.start