subaru 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []