subaru 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1dd0e11ac22422831de4f5b6852fd029792f48a7
4
+ data.tar.gz: 769c66c9d6da8deb6292eabe37f21d70f1703c24
5
+ SHA512:
6
+ metadata.gz: ccffba240eb408226b094ed8dff4cf2f4d357864419cdbd497e7d90f47d56c64980a04493bbe4ba4395279205e44cd62bbda59c2bad1be601d0827810dfccb2f
7
+ data.tar.gz: 06a7b92779b99595e2328de537251c187ae90f38f8502f84719974daeb61ca345307202bfb411c4bef31ca4b00fcd97e828bc80b31e6620200ff365fdd35b200
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Ken J.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Subaru
2
+
3
+ A gem to run RESTful (kinda) API server to industrial relays.
4
+
5
+ The `subaru` command will launch a [Sinatra](http://www.sinatrarb.com) based server, functioning as a gateway to your web-enabled relays.
6
+
7
+ ## Supported Relays
8
+
9
+ - Xytronix (a.k.a. ControlByWeb)
10
+ - WebRelay
11
+ - WebRelay-10
12
+
13
+ I welcome requests for other relay equipment. If development/evaluation units can be provided, that would increase the likelyhood of support.
14
+
15
+ ## Requirements
16
+
17
+ - Ruby 2.0.0 <=
18
+ - [Kajiki](http://www.kenj.rocks/kajiki/) 1.1 <=
19
+ - Sinatra 1.4 <=
20
+
21
+ ## Getting Started
22
+
23
+ ### Install
24
+
25
+ ```
26
+ $ gem install subaru
27
+ ```
28
+
29
+ ### Configure
30
+
31
+ Store configuration in a YAML file. If you need a template, just try to start Subaru without the `--config` option and it'll output an example.
32
+
33
+ ```yaml
34
+ ---
35
+ :global:
36
+ :pretty_json: YES
37
+ :auth_tokens:
38
+ :any:
39
+ - abcd # This is the auth token. List as many as you want, or remove it to disable auth.
40
+
41
+ :devices:
42
+ factory: # This is the device name to use in the URL.
43
+ :definition: Xytronix::WebRelay
44
+ :url: http://192.168.0.10
45
+ :password: password
46
+ :read_timeout: 15
47
+ ```
48
+
49
+ ### Run
50
+
51
+ ```
52
+ $ subaru start -c config.yml
53
+ ```
54
+
55
+ ### Consume
56
+
57
+ `GET` to read the state.
58
+
59
+ ```
60
+ $ curl http://subaru/factory
61
+ {
62
+ "relay": "off",
63
+ "input": "off"
64
+ }
65
+ ```
66
+
67
+ `PUT` to set the state.
68
+
69
+ ```
70
+ $ curl -X PUT http://subaru/factory -d '{"relay":"on"}'
71
+ {
72
+ "relay": "on",
73
+ "input": "off"
74
+ }
75
+ ```
76
+
77
+ ## Security
78
+
79
+ For obvious reasons, you should not expose this server to the public. If so, at least protect behind a reverse proxy, like NGINX, and/or require auth over HTTPS.
data/bin/subaru ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'kajiki'
3
+ require 'subaru'
4
+
5
+
6
+ opts = Kajiki.preset_options(:server, {config: true, error: false, user: false})
7
+
8
+ Kajiki.run(opts) do |cmd|
9
+ case cmd
10
+ when 'start'
11
+ Subaru::Server.run!(opts)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ ---
2
+ :global:
3
+ :pretty_json: YES
4
+ :auth_tokens:
5
+ :any:
6
+ - abcd
7
+
8
+ :devices:
9
+ factory:
10
+ :definition: Xytronix::WebRelay
11
+ :url: http://192.168.0.10
12
+ :password: password
13
+ :read_timeout: 15
data/lib/subaru.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'sinatra/base'
2
+ require 'yaml'
3
+ require 'subaru/helper'
4
+
5
+
6
+ module Subaru
7
+
8
+ # The Sinatra server
9
+ class Server < Sinatra::Base
10
+
11
+ def self.run!(opts = {})
12
+ unless opts[:config]
13
+ puts "Configuration file required. Example:\n\n"
14
+ puts IO.read(File.expand_path('../config_temp.yml', __FILE__))
15
+ abort
16
+ end
17
+ config = YAML.load_file(opts[:config])
18
+ super(bind: opts[:address], port: opts[:port], subaru_config: config)
19
+ end
20
+
21
+ helpers Helper
22
+
23
+ configure do
24
+ disable :static
25
+ end
26
+
27
+ before do
28
+ halt 401 unless valid_token?(params[:auth])
29
+ end
30
+
31
+ get '/:device' do |device|
32
+ dev = device_with_name(device)
33
+ halt 404 unless dev
34
+ res = dev.read
35
+ halt 500 unless res
36
+ json_with_object(res)
37
+ end
38
+
39
+ put '/:device' do |device|
40
+ dev = device_with_name(device)
41
+ halt 404 unless dev
42
+ request.body.rewind # in case someone already read it
43
+ res = dev.write(request.body.read)
44
+ halt 500 unless res
45
+ json_with_object(res)
46
+ end
47
+
48
+ not_found do
49
+ json_with_object({message: 'Not found.'})
50
+ end
51
+
52
+ error 401 do
53
+ json_with_object({message: 'Auth required.'})
54
+ end
55
+
56
+ error do
57
+ status 500
58
+ json_with_object({message: 'Sorry, internal error.'})
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,155 @@
1
+ require 'open-uri'
2
+ require 'uri'
3
+ require 'rexml/document'
4
+
5
+
6
+ module Subaru
7
+
8
+ module Definitions
9
+
10
+ module Xytronix
11
+
12
+ module Common
13
+ attr_accessor :options
14
+
15
+ # @param url [String] device url; e.g., `http://10.1.1.11:8888`.
16
+ # @param opts [Hash] the device options.
17
+ def initialize(opts = {})
18
+ @options = opts
19
+ end
20
+
21
+ # Generate query string.
22
+ # @param query_hash [hash] resources and their state to set to.
23
+ # @return [String] query string.
24
+ def generate_query(query_hash)
25
+ return nil unless query_hash.class == Hash
26
+ h = {}
27
+ query_hash.each do |k, v|
28
+ case v
29
+ when "off"
30
+ h["#{k}State"] = 0
31
+ when "on"
32
+ h["#{k}State"] = 1
33
+ end
34
+ end
35
+ return nil if h.empty?
36
+ return URI.encode_www_form(h)
37
+ end
38
+
39
+ # Access the device.
40
+ # @param query [String] encoded query string.
41
+ # @return [Object] XML document returned from the device.
42
+ def send_request(query = nil)
43
+ uri = "#{@options[:url]}/stateFull.xml"
44
+ uri << "?#{query}" if query
45
+ o = {}
46
+ o[:http_basic_authentication] = [nil, @options[:password]] if @options[:password]
47
+ o[:read_timeout] = @options[:read_timeout] if @options[:read_timeout]
48
+ response = open(uri, o)
49
+ return REXML::Document.new(response.read)
50
+ rescue => e
51
+ puts e.message
52
+ return nil
53
+ end
54
+
55
+ # Search the XML document.
56
+ # @param doc [Object] XML document.
57
+ # @param resource [String] resource name to search.
58
+ # @param ch [Fixnum] resource channel number.
59
+ # @return [String] state of the resource; e.g., `on`, `off`.
60
+ def state_from_xml(doc, resource, ch = nil)
61
+ case REXML::XPath.first(doc, "//datavalues/#{resource}#{ch}state").text.to_i
62
+ when 0
63
+ s = "off"
64
+ when 1
65
+ s = "on"
66
+ else
67
+ s = "unknown"
68
+ end
69
+ return s
70
+ rescue => e
71
+ puts e.message
72
+ return nil
73
+ end
74
+
75
+ end
76
+
77
+ class WebRelay
78
+ include Common
79
+
80
+ # Read resources from device.
81
+ # @return [Hash] resources and their state.
82
+ def read
83
+ return parse_xml(send_request)
84
+ end
85
+
86
+ # Set resource state of a device.
87
+ # @param data [String] POST/PUT data.
88
+ # @return [Hash] resulting resources and their state.
89
+ def write(data)
90
+ q = generate_query(JSON.parse(data))
91
+ return nil unless q
92
+ return parse_xml(send_request(q))
93
+ rescue => e
94
+ puts e.message
95
+ return nil
96
+ end
97
+
98
+ # Parse XML document response.
99
+ # @param xml [Object] XML document.
100
+ # @return [Hash]
101
+ def parse_xml(xml)
102
+ results = {}
103
+ ['relay', 'input'].each do |r|
104
+ results[r] = state_from_xml(xml, r)
105
+ end
106
+ return results
107
+ rescue => e
108
+ puts e.message
109
+ return nil
110
+ end
111
+
112
+ end
113
+
114
+ class WebRelay10
115
+ include Common
116
+
117
+ # Read resources from device.
118
+ # @return [Hash] resources and their state.
119
+ def read
120
+ return parse_xml(send_request)
121
+ end
122
+
123
+ # Set resource state of a device.
124
+ # @param data [String] POST/PUT data.
125
+ # @return [Hash] resulting resources and their state.
126
+ def write(data)
127
+ q = generate_query(JSON.parse(data))
128
+ return nil unless q
129
+ return parse_xml(send_request(q))
130
+ rescue => e
131
+ puts e.message
132
+ return nil
133
+ end
134
+
135
+ # Parse XML document response.
136
+ # @param xml [Object] XML document.
137
+ # @return [Hash]
138
+ def parse_xml(xml)
139
+ results = {}
140
+ (1..10).each do |ch|
141
+ results["relay#{ch}"] = state_from_xml(xml, 'relay', ch)
142
+ end
143
+ return results
144
+ rescue => e
145
+ puts e.message
146
+ return nil
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,48 @@
1
+ require 'json'
2
+ require 'subaru/d-xytronix'
3
+
4
+
5
+ module Subaru
6
+
7
+ # Sinatra helper
8
+ module Helper
9
+
10
+ # Convert object into JSON.
11
+ def json_with_object(object, opts = {pretty: settings.subaru_config[:global][:pretty_json]})
12
+ return '{}' if object.nil?
13
+ if opts[:pretty] == true
14
+ opts = {
15
+ indent: ' ',
16
+ space: ' ',
17
+ object_nl: "\n",
18
+ array_nl: "\n"
19
+ }
20
+ end
21
+ JSON.fast_generate(object, opts)
22
+ rescue => e
23
+ puts e.message
24
+ raise
25
+ end
26
+
27
+ # Validate auth token, if configured.
28
+ def valid_token?(token, method = :any)
29
+ tokens = settings.subaru_config[:global][:auth_tokens][method]
30
+ return true if tokens.nil?
31
+ tokens.include?(token)
32
+ end
33
+
34
+ # Create device object with name
35
+ # @param name [String]
36
+ # @return [Object]
37
+ def device_with_name(name)
38
+ c = settings.subaru_config[:devices][name]
39
+ return Object.const_get("Subaru::Definitions::#{c[:definition]}").new(c) if c
40
+ rescue => e
41
+ puts e.message
42
+ return nil
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
data/subaru.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "subaru"
3
+ s.version = "1.0.0"
4
+ s.authors = ["Ken J."]
5
+ s.email = ["kenjij@gmail.com"]
6
+ s.description = %q{RESTful API to industrial web relays}
7
+ s.summary = %q{RESTful API server gem to industrial web relays.}
8
+ s.homepage = "https://github.com/kenjij/subaru"
9
+ s.license = "MIT"
10
+
11
+ s.files = `git ls-files`.split($/)
12
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
13
+ s.require_paths = ["lib"]
14
+
15
+ s.add_runtime_dependency "kajiki", "~> 1.1"
16
+ s.add_runtime_dependency "sinatra", "~> 1.4"
17
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: subaru
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ken J.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kajiki
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ description: RESTful API to industrial web relays
42
+ email:
43
+ - kenjij@gmail.com
44
+ executables:
45
+ - subaru
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - LICENSE
50
+ - README.md
51
+ - bin/subaru
52
+ - lib/config_temp.yml
53
+ - lib/subaru.rb
54
+ - lib/subaru/d-xytronix.rb
55
+ - lib/subaru/helper.rb
56
+ - subaru.gemspec
57
+ homepage: https://github.com/kenjij/subaru
58
+ licenses:
59
+ - MIT
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 2.4.3
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: RESTful API server gem to industrial web relays.
81
+ test_files: []