sunscout 0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 932d2d5473a423b5839198dde6179124b13c5090
4
+ data.tar.gz: ea0ca9ec4af624858f71f976a50b3abf6e63a7de
5
+ SHA512:
6
+ metadata.gz: 88afb8ab77d532ddc8cca0344259904d32856c33c4f836d282b831b59c6aefebc6703a08e83c1b0478a451621ccb253d013d0c1f1f1c094ba78b12a39c487823
7
+ data.tar.gz: a7f2a597d77e64702d9dc997e7602b62b0b499db5c8828ba27733d1557a1b6a17978eefd09544306c15c462d2eb88d9c6798200efd5672f5a37a258ee9603637
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sunscout.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ Copyright 2016 Michael Senn
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,41 @@
1
+ # Sunscout
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sunscout`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sunscout'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install sunscout
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sunscout.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [Apache-2.0 License](http://opensource.org/licenses/Apache-2.0).
41
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sunscout"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'sunscout/solar_log'
4
+
5
+ module Sunscout
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'solar_log/client'
4
+ require_relative 'solar_log/solar_log'
5
+
6
+ module Sunscout
7
+ # Classes to interact with SolarLog HTTP API.
8
+ #
9
+ # - {SolarLog::Client} Low-Level client which returns API data directly, remapped to human-readable keys
10
+ # - {SolarLog::SolarLog} High-level client, which offers various convenience functions.
11
+ # @example
12
+ # #!/usr/bin/env ruby
13
+ # # coding: utf-8
14
+ #
15
+ # require 'sunscout'
16
+ #
17
+ # puts 'Querying data from SolarLog API'
18
+ # solar_log = Sunscout::SolarLog::SolarLog.new('http://10.0.0.10')
19
+ #
20
+ # puts "Data from: #{ solar_log.time.iso8601 }"
21
+ # puts "AC Power: #{ solar_log.power_ac }W (DC Power: #{ solar_log.power_dc }W, #{ (solar_log.efficiency*100).round(0) }% efficiency, #{ solar_log.alternator_loss }W loss)"
22
+ #
23
+ # puts "Current usage: #{ solar_log.consumption_ac }W (#{ (solar_log.usage*100).round(0) }%)"
24
+ module SolarLog
25
+ end
26
+ end
@@ -0,0 +1,100 @@
1
+ # coding: utf-8
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ REQUEST_QUERY = 'getjp'
8
+ REQUEST_PAYLOAD = { 801 => { 170 => nil } }
9
+
10
+ module Sunscout
11
+ module SolarLog
12
+ # Low-level binding to the SolarLog HTTP API
13
+ # @example Basic usage
14
+ # require 'sunscout'
15
+ # c = Sunscout::SolarLog::Client.new('http://10.60.1.10')
16
+ # data = c.get_data()
17
+ # puts "Current power output: #{ data[:power_ac] }W"
18
+ class Client
19
+ # Initialize a new instance of the class.
20
+ # @param host [String] URI of the SolarLog web interface.
21
+ def initialize(host)
22
+ @host = host
23
+ end
24
+
25
+ # Retrieve data from the HTTP API.
26
+ # @return [Hash<Symbol, String|Integer>] Hash containing retrieved data
27
+ def get_data
28
+ uri = build_uri
29
+ req = build_request(uri)
30
+ data = send_request(req, uri)
31
+
32
+ data
33
+ end
34
+
35
+ private
36
+ # Create URI of HTTP endpoint
37
+ def build_uri
38
+ URI("#{ @host }/#{ REQUEST_QUERY }")
39
+ end
40
+
41
+ # Build HTTP POST request
42
+ # @param uri [URI] URI of HTTP endpoint
43
+ def build_request(uri)
44
+ req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
45
+ req.body = JSON.dump(REQUEST_PAYLOAD)
46
+
47
+ req
48
+ end
49
+
50
+ # Send HTTP request to URI
51
+ # @param req [Net::HTTP::Post] HTTP request
52
+ # @param uri [URI] URI of HTTP endpoint
53
+ # @return [Hash] Raw data retrieved by HTTP API
54
+ def send_request(req, uri)
55
+ res = Net::HTTP.start(uri.hostname, uri.port) do |http|
56
+ http.request(req)
57
+ end
58
+
59
+ # Todo: Exception handling:
60
+ # - catching in case of failure (DNS, timeout, ...)
61
+ # - throwing in case of API failure
62
+ case res
63
+ when Net::HTTPSuccess
64
+ data = parse_data(JSON.parse(res.body))
65
+ else
66
+ data = {}
67
+ end
68
+
69
+ data
70
+ end
71
+
72
+ # Remap raw API data to human-readable data.
73
+ # @param data [Hash] Raw API data
74
+ # @return [Hash] Human-readable data
75
+ def parse_data(data)
76
+ data = data.fetch('801').fetch('170')
77
+
78
+ out = {
79
+ time: data.fetch('100'),
80
+ power_ac: data.fetch('101'),
81
+ power_dc: data.fetch('102'),
82
+ voltage_ac: data.fetch('103'),
83
+ voltage_dc: data.fetch('104'),
84
+ yield_day: data.fetch('105'),
85
+ yield_yesterday: data.fetch('106'),
86
+ yield_month: data.fetch('107'),
87
+ yield_year: data.fetch('108'),
88
+ yield_total: data.fetch('109'),
89
+ consumption_ac: data.fetch('110'),
90
+ consumption_day: data.fetch('111'),
91
+ consumption_yesterday: data.fetch('112'),
92
+ consumption_month: data.fetch('113'),
93
+ consumption_year: data.fetch('114'),
94
+ consumption_total: data.fetch('115'),
95
+ power_total: data.fetch('116'),
96
+ }
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,172 @@
1
+ # coding: utf-8
2
+
3
+ require 'date'
4
+
5
+ module Sunscout
6
+ module SolarLog
7
+ # High-level binding to the SolarLog HTTP API
8
+ # @example Basic usage
9
+ # require 'sunscout'
10
+ # sl = Sunscout::SolarLog::SolarLog.new('http://10.60.1.10')
11
+ # puts "AC power: #{ sl.power_ac }W (DC: #{ sl.power_dc }W, Efficiency #{ (sl.efficiency*100).round(0) }%)"
12
+ # puts "Remaining power: #{ sl.power_available }W"
13
+ class SolarLog
14
+ # Timestamp of the data.
15
+ # @return [DateTime]
16
+ attr_reader :time
17
+
18
+ # AC power in W
19
+ # @return [Fixnum]
20
+ attr_reader :power_ac
21
+ # DC power in W
22
+ # @return [Fixnum]
23
+ attr_reader :power_dc
24
+ # Maximum DC power in W
25
+ # @return [Fixnum]
26
+ attr_reader :power_total
27
+
28
+ # AC voltage in V
29
+ # @return [Fixnum]
30
+ attr_reader :voltage_ac
31
+ # DC voltage in V
32
+ # @return [Fixnum]
33
+ attr_reader :voltage_dc
34
+
35
+ # Today's yield in Wh
36
+ # @return [Fixnum]
37
+ attr_reader :yield_day
38
+ # Yesterday's yield in WH
39
+ # @return [Fixnum]
40
+ attr_reader :yield_yesterday
41
+ # This month's yield in Wh
42
+ # @return [Fixnum]
43
+ attr_reader :yield_month
44
+ # This year's yield in Wh
45
+ # @return [Fixnum]
46
+ attr_reader :yield_year
47
+ # Total yield in Wh
48
+ # @return [Fixnum]
49
+ attr_reader :yield_total
50
+
51
+ # Current consumption in W
52
+ # @return [Fixnum]
53
+ attr_reader :consumption_ac
54
+ # Today's consumption in Wh
55
+ # @return [Fixnum]
56
+ attr_reader :consumption_day
57
+ # Yesterday's consumption in Wh
58
+ # @return [Fixnum]
59
+ attr_reader :consumption_yesterday
60
+ # This month's consumption in Wh
61
+ # @return [Fixnum]
62
+ attr_reader :consumption_month
63
+ # This year's consumption in Wh
64
+ # @return [Fixnum]
65
+ attr_reader :consumption_year
66
+ # Total consumption in Wh
67
+ # @return [Fixnum]
68
+ attr_reader :consumption_total
69
+
70
+ # Initialize a new instance of the class.
71
+ #
72
+ # This also immediately queries data from the SolarLog API.
73
+ #
74
+ # @param host [String] URI of the SolarLog web interface
75
+ def initialize(host)
76
+ client = Sunscout::SolarLog::Client.new(host)
77
+ data = client.get_data
78
+
79
+ @time = DateTime.strptime(data.fetch(:time), '%d.%m.%y %H:%M:%s')
80
+
81
+ @power_ac = data.fetch :power_ac
82
+ @power_dc = data.fetch :power_dc
83
+ @power_total = data.fetch :power_total
84
+
85
+ @voltage_ac = data.fetch :voltage_ac
86
+ @voltage_dc = data.fetch :voltage_dc
87
+
88
+ @yield_day = data.fetch :yield_day
89
+ @yield_yesterday = data.fetch :yield_yesterday
90
+ @yield_month = data.fetch :yield_month
91
+ @yield_year = data.fetch :yield_year
92
+ @yield_total = data.fetch :yield_total
93
+
94
+ @consumption_ac = data.fetch :consumption_ac
95
+ @consumption_day = data.fetch :consumption_day
96
+ @consumption_yesterday = data.fetch :consumption_yesterday
97
+ @consumption_month = data.fetch :consumption_month
98
+ @consumption_year = data.fetch :consumption_year
99
+ @consumption_total = data.fetch :consumption_total
100
+ end
101
+
102
+ # Efficiency of DC to AC conversion.
103
+ # @return [Float] Efficiency as percentage between 0 and 1.
104
+ #
105
+ # @example
106
+ # solar_log.power_ac #=> 94
107
+ # solar_log.power_dc #=> 100
108
+ # solar_log.efficiency #=> 0.94
109
+ def efficiency
110
+ return 0 if @power_dc == 0
111
+ power_ac.to_f / power_dc
112
+ end
113
+
114
+ # Loss of DC to AC conversion.
115
+ # @return [Fixnum] Loss of alternator in Watt.
116
+ #
117
+ # @example
118
+ # solar_log.power_ac #=> 94
119
+ # solar_log.power_dc #=> 100
120
+ # solar_log.alternator_loss #=> 6
121
+ def alternator_loss
122
+ power_dc - power_ac
123
+ end
124
+
125
+ # Usage of AC power.
126
+ # @return [Float] Usage of AC power as percentage. >1 if more power consumed than generated.
127
+ #
128
+ # @example Usage <100%
129
+ # solar_log.consumption_ac #=> 50
130
+ # solar_log.power_ac #=> 100
131
+ # solar_log.usage #=> 0.5
132
+ #
133
+ # @example Usage >100%
134
+ # solar_log.consumption_ac #=> 200
135
+ # solar_log.power_ac #=> 100
136
+ # solar_log.usage #=> 2
137
+ def usage
138
+ return 0 if @power_ac == 0
139
+ consumption_ac.to_f / power_ac
140
+ end
141
+
142
+ # Surplus AC power.
143
+ # @return [Fixnum] Surplus AC power in Watt. Negative if more power consumed than generated.
144
+ #
145
+ # @example Usage <100%
146
+ # solar_log.consumption_ac #=> 50
147
+ # solar_log.power_ac #=> 100
148
+ # solar_log.power_available #=> 50
149
+ #
150
+ # @example Usage >100%
151
+ # solar_log.consumption_ac #=> 200
152
+ # solar_log.power_ac #=> 100
153
+ # solar_log.power_available #=> -100
154
+ def power_available
155
+ power_ac - consumption_ac
156
+ end
157
+
158
+ # Capacity of peak power generation.
159
+ # @return [Float] Percentage of peak power generation.
160
+ #
161
+ # @example
162
+ # solar_log.power_dc #=> 8000
163
+ # solar_log.power_total #=> 10000
164
+ # solar_log.capacity #=> 0.8
165
+ def capacity
166
+ return 0 if power_total == 0
167
+ power_dc / power_total
168
+ end
169
+ end
170
+ end
171
+ end
172
+
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sunscout"
7
+ spec.version = '0.1'
8
+ spec.authors = ["Michael Senn"]
9
+ spec.email = ["michael@morrolan.ch"]
10
+
11
+ spec.summary = %q{Binding to Solarlog HTTP API}
12
+ # spec.description = %q{}
13
+ # spec.homepage = ""
14
+ spec.license = "Apache-2.0"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.12"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ spec.add_development_dependency "yard"
25
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sunscout
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Michael Senn
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - michael@morrolan.ch
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/sunscout.rb
86
+ - lib/sunscout/solar_log.rb
87
+ - lib/sunscout/solar_log/client.rb
88
+ - lib/sunscout/solar_log/solar_log.rb
89
+ - sunscout.gemspec
90
+ homepage:
91
+ licenses:
92
+ - Apache-2.0
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.5.1
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Binding to Solarlog HTTP API
114
+ test_files: []