gizwits_sac 0.1.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: dc32b80766971cbb24e854e5903065995eabab11
4
+ data.tar.gz: 1cc278402bc41adb0007f5c2674e46c21b23cde6
5
+ SHA512:
6
+ metadata.gz: f6cd787363965c866202782d1e5ad3c248db317ec900727c7fbdf03328f7d4a5c673d6592ba70f46e5c8969708922396f57bb032ea67c0ec552d344038e4a5d3
7
+ data.tar.gz: 51fb4dcb555bce41b6117920508c7b7facf35d2afbc0b213a8f93a3eb5c8084ef805874230d05d35219bde166db78371f18e0a071033bd3e87a23aaa90c866d3
data/.gitignore ADDED
@@ -0,0 +1,26 @@
1
+ # Compiled Object files, Static and Dynamic libs (Shared Objects)
2
+ *.o
3
+ *.a
4
+ *.so
5
+
6
+ # Folders
7
+ _obj
8
+ _test
9
+
10
+ # Architecture specific extensions/prefixes
11
+ *.[568vq]
12
+ [568vq].out
13
+
14
+ *.cgo1.go
15
+ *.cgo2.c
16
+ _cgo_defun.c
17
+ _cgo_gotypes.go
18
+ _cgo_export.*
19
+
20
+ _testmain.go
21
+
22
+ *.exe
23
+ *.test
24
+ *.prof
25
+
26
+ /pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gizwits_sac.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,46 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gizwits_sac (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.1)
10
+ diff-lcs (1.3)
11
+ method_source (0.8.2)
12
+ pry (0.10.4)
13
+ coderay (~> 1.1.0)
14
+ method_source (~> 0.8.1)
15
+ slop (~> 3.4)
16
+ pry-nav (0.2.4)
17
+ pry (>= 0.9.10, < 0.11.0)
18
+ rake (10.5.0)
19
+ rspec (3.6.0)
20
+ rspec-core (~> 3.6.0)
21
+ rspec-expectations (~> 3.6.0)
22
+ rspec-mocks (~> 3.6.0)
23
+ rspec-core (3.6.0)
24
+ rspec-support (~> 3.6.0)
25
+ rspec-expectations (3.6.0)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.6.0)
28
+ rspec-mocks (3.6.0)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.6.0)
31
+ rspec-support (3.6.0)
32
+ slop (3.6.0)
33
+
34
+ PLATFORMS
35
+ ruby
36
+
37
+ DEPENDENCIES
38
+ bundler (~> 1.13)
39
+ gizwits_sac!
40
+ pry
41
+ pry-nav
42
+ rake (~> 10.0)
43
+ rspec (~> 3.0)
44
+
45
+ BUNDLED WITH
46
+ 1.13.6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Abel
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.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # GizwitsSac-rb
2
+
3
+ GizwitsSac is short for Gizwits Snoti API Client, and GizwitsSac-rb just a Ruby version, which I hope it can help you more easily to connect to [Gizwits Snoti API](http://docs.gizwits.com/zh-cn/Cloud/NotificationAPI.html).
4
+
5
+ ### [中文说明](https://github.com/AbelLai/gizwits_sac_rb/blob/master/README.zh_cn.md)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'gizwits_sac'
13
+ ```
14
+
15
+ ## Usage
16
+ ### 1. GizwitsSac::SnotiClient
17
+ Simply you can just use GizwitsSac::SnotiClient, and focus on the event via callback as the example below. (**GizwitsSac::SnotiClient will handle heartbeat for you in ervery seconds you set.**)
18
+
19
+ ```ruby
20
+ require "gizwits_sac"
21
+
22
+ event_push_handler = Proc.new do |event_push_data|
23
+ # Here it just care the event *device_status_raw*.
24
+ if event_push_data["event_type"] == "device_status_raw"
25
+ puts "event_push_data =====> #{event_push_data}"
26
+ end
27
+ end
28
+ remote_ctrl_handler = Proc.new do
29
+ get_your_remote_ctrl_req
30
+ end
31
+ remote_ctrl_res_handler = Proc.new do |r_ctrl_res_data|
32
+ puts "r_ctrl_res_data =====> #{r_ctrl_res_data}"
33
+ end
34
+
35
+ client = GizwitsSac::SnotiClient.new({
36
+ event_push: event_push_handler,
37
+ remote_ctrl: remote_ctrl_handler,
38
+ remote_ctrl_res: remote_ctrl_res_handler,
39
+ heartbeat_interval: 60, # default value is 5
40
+ retry_count: 10, # default value is 5
41
+ logger: Logger.new(STDOUT),
42
+ host: "snoti.gizwits.com",
43
+ port: "2017",
44
+ connect_timeout: 3, # default value is 3 seconds
45
+ read_timeout: 3, # default value is 3 seconds
46
+ write_timeout: 3, # default value is 3 seconds
47
+ prefetch_count: 50, # default value is 50
48
+ auth_data: [
49
+ {
50
+ product_key: "your_product_key_here",
51
+ auth_id: "your_auth_id_here",
52
+ auth_secret: "your_auth_secret_here",
53
+ subkey: "your_subkey_here",
54
+ events: ['event_you_care_about', 'event_you_care_about', ...]
55
+ }
56
+ ]
57
+ })
58
+
59
+ client.start
60
+ ```
61
+
62
+ ### 2. GizwitsSac::SnotiSocket
63
+ Another way, you can use GizwitsSac::SnotiSocket, and build a client as you like. Here is a very very simple example as below.
64
+ ```ruby
65
+ require "gizwits_sac"
66
+
67
+ socket = GizwitsocketC::SnotiSocket.new({
68
+ host: "snoti.gizwits.com",
69
+ port: "2017",
70
+ connect_timeout: 3, # default value is 3 seconds
71
+ read_timeout: 3, # default value is 3 seconds
72
+ write_timeout: 3, # default value is 3 seconds
73
+ prefetch_count: 50, # default value is 50
74
+ auth_data: [
75
+ {
76
+ product_key: "your_product_key_here",
77
+ auth_id: "your_auth_id_here",
78
+ auth_secret: "your_auth_secret_here",
79
+ subkey: "your_subkey_here",
80
+ events: ['event_you_care_about', 'event_you_care_about', ...]
81
+ }
82
+ ]
83
+ })
84
+
85
+ socket.connect
86
+ if socket.login_ok?
87
+ puts "login ok"
88
+ loop do
89
+ data = socket.read
90
+ puts data
91
+ sleep 2
92
+ end
93
+ else
94
+ puts "login failed"
95
+ end
96
+ ```
97
+ ## TODO: Unit Test
98
+
99
+
100
+
101
+
102
+
data/README.zh_cn.md ADDED
@@ -0,0 +1,103 @@
1
+ # GizwitsSac-rb
2
+
3
+ GizwitsSac其实是Gizwits Snoti API Client的缩写,一个Ruby版本。
4
+
5
+
6
+ ## 安装
7
+
8
+ 往你的Gemfile加入以下这行:
9
+ ```ruby
10
+ gem 'gizwits_sac'
11
+ ```
12
+
13
+ ## 用法
14
+ ### 1. GizwitsSac::SnotiClient
15
+ 使用GizwitsSac::SnotiClient,SnotiClient会帮你处理连接的部分以及心跳部分,你只用关心相关事件以及往连接塞你需要发送的远程控制命令。
16
+
17
+ 例子:
18
+ ```ruby
19
+ require "gizwits_sac"
20
+
21
+ event_push_handler = Proc.new do |event_push_data|
22
+ # Here it just care the event *device_status_raw*.
23
+ if event_push_data["event_type"] == "device_status_raw"
24
+ puts "event_push_data =====> #{event_push_data}"
25
+ end
26
+ end
27
+ remote_ctrl_handler = Proc.new do
28
+ get_your_remote_ctrl_req
29
+ end
30
+ remote_ctrl_res_handler = Proc.new do |r_ctrl_res_data|
31
+ puts "r_ctrl_res_data =====> #{r_ctrl_res_data}"
32
+ end
33
+
34
+ client = GizwitsSac::SnotiClient.new({
35
+ event_push: event_push_handler,
36
+ remote_ctrl: remote_ctrl_handler,
37
+ remote_ctrl_res: remote_ctrl_res_handler,
38
+ heartbeat_interval: 60, # default value is 5
39
+ retry_count: 10, # default value is 5
40
+ logger: Logger.new(STDOUT),
41
+ host: "snoti.gizwits.com",
42
+ port: "2017",
43
+ connect_timeout: 3, # default value is 3 seconds
44
+ read_timeout: 3, # default value is 3 seconds
45
+ write_timeout: 3, # default value is 3 seconds
46
+ prefetch_count: 50, # default value is 50
47
+ auth_data: [
48
+ {
49
+ product_key: "your_product_key_here",
50
+ auth_id: "your_auth_id_here",
51
+ auth_secret: "your_auth_secret_here",
52
+ subkey: "your_subkey_here",
53
+ events: ['event_you_care_about', 'event_you_care_about', ...]
54
+ }
55
+ ]
56
+ })
57
+
58
+ client.start
59
+ ```
60
+
61
+ ### 2. GizwitsSac::SnotiSocket
62
+ GizwitsSac封装了一个SnotiSocket,你可以基于SnotiSocket实现自己的client.
63
+
64
+ 例子:
65
+ ```ruby
66
+ require "gizwits_sac"
67
+
68
+ socket = GizwitsocketC::SnotiSocket.new({
69
+ host: "snoti.gizwits.com",
70
+ port: "2017",
71
+ connect_timeout: 3, # default value is 3 seconds
72
+ read_timeout: 3, # default value is 3 seconds
73
+ write_timeout: 3, # default value is 3 seconds
74
+ prefetch_count: 50, # default value is 50
75
+ auth_data: [
76
+ {
77
+ product_key: "your_product_key_here",
78
+ auth_id: "your_auth_id_here",
79
+ auth_secret: "your_auth_secret_here",
80
+ subkey: "your_subkey_here",
81
+ events: ['event_you_care_about', 'event_you_care_about', ...]
82
+ }
83
+ ]
84
+ })
85
+
86
+ socket.connect
87
+ if socket.login_ok?
88
+ puts "login ok"
89
+ loop do
90
+ data = socket.read
91
+ puts data
92
+ sleep 2
93
+ end
94
+ else
95
+ puts "login failed"
96
+ end
97
+ ```
98
+ ## TODO: Unit Test
99
+
100
+
101
+
102
+
103
+
data/Rakefile ADDED
@@ -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
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gizwits_sac"
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
data/bin/setup ADDED
@@ -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,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gizwits_sac/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gizwits_sac"
8
+ spec.version = GizwitsSac::VERSION
9
+ spec.authors = ["Abel Lai"]
10
+ spec.email = ["yilaqingfeng@gamil.com"]
11
+
12
+ spec.summary = %q{Gizwits Snoti API Client}
13
+ spec.description = %q{Gizwits Snoti API Client for Ruby.}
14
+ spec.homepage = "https://github.com/AbelLai/gizwits_sac_rb"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against " \
22
+ "public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features)/})
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.13"
33
+ spec.add_development_dependency "rake", "~> 10.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ spec.add_development_dependency "pry"
36
+ spec.add_development_dependency "pry-nav"
37
+ end
@@ -0,0 +1,19 @@
1
+ require "socket"
2
+
3
+ # GizwitsSac = Gizwits Snoti API Client
4
+ module GizwitsSac
5
+ class LoginError < SocketError
6
+ end
7
+
8
+ class ConnTimeoutError < SocketError
9
+ end
10
+
11
+ class ReadTimeoutError < SocketError
12
+ end
13
+
14
+ class WriteTimeoutError < SocketError
15
+ end
16
+
17
+ class ConnFailedError < SocketError
18
+ end
19
+ end
@@ -0,0 +1,191 @@
1
+ require "logger"
2
+ require "thread"
3
+
4
+ # Example for params when new a SnotiClient:
5
+ # options = {
6
+ # event_push: (Proc.new {|event_push_data| }),
7
+ # remote_ctrl: (Proc.new { return nil }),
8
+ # remote_ctrl_res: (Proc.new {|r_ctrl_data| }),
9
+ # error_res: (Proc.new {|err_data| }),
10
+ # heartbeat_interval: 60,
11
+ # logger: Logger.new("xxxx"),
12
+ # retry_count: 10,
13
+ # host: "xxx",
14
+ # port: "xxx",
15
+ # timeout: 3,
16
+ # read_timeout: 3,
17
+ # write_timeout: 3,
18
+ # prefetch_count: 50,
19
+ # auth_data: [
20
+ # {
21
+ # product_key: "xxxx",
22
+ # auth_id: "xxxx",
23
+ # auth_secret: "xxxx",
24
+ # subkey: "xxxx",
25
+ # events: ['xxx', 'xxx']
26
+ # }
27
+ # ]
28
+ # }
29
+
30
+ # GizwitsSac = Gizwits Snoti API Client
31
+ module GizwitsSac
32
+ class SnotiClient
33
+ def initialize(options)
34
+ @event_push = options.delete(:event_push) || (Proc.new {|event_push_data| })
35
+ @remote_ctrl = options.delete(:remote_ctrl) || (Proc.new { return nil })
36
+ @remote_ctrl_res = options.delete(:remote_ctrl_res) || (Proc.new {|r_ctrl_data| })
37
+ @error_res = options[:error_res] || (Proc.new {|err_data| })
38
+ @retry_count = options.delete(:retry_count) || 5
39
+ @heartbeat_interval = options.delete(:heartbeat_interval) || 60
40
+ @logger = options.delete(:logger) || Logger.new(STDOUT)
41
+ @heartbeat_thread = nil
42
+ @remote_ctrl_thread = nil
43
+ @socket = SnotiSocket.new(options)
44
+ @exited = false
45
+ end
46
+
47
+ def start
48
+ listen_shut_down
49
+
50
+ connect_count = 0
51
+ begin
52
+ connect_count += 1
53
+ # 1. Connect to Gizwits Snoti API
54
+ @socket.connect
55
+ # 2. Login to Gizwits Snoti API and Check
56
+ if @socket.login_ok?
57
+ # 3. Exchange data via SnotiSocket
58
+ exchange
59
+ else
60
+ raise LoginError
61
+ end
62
+ rescue Exception => e
63
+ if connect_count < @retry_count
64
+ retry
65
+ else
66
+ # It needs to kill the backgroud thread after all retry failed
67
+ dispose
68
+ raise e
69
+ end
70
+ end
71
+ end
72
+
73
+ private
74
+ def exchange
75
+ # 1. Start heartbeat
76
+ heartbeat
77
+ # 2. Start remote control in another thread
78
+ invoke_remote_ctrl
79
+ # 3. Loop and fetch data from Gizwits Snoti API
80
+ smart_read
81
+ end
82
+
83
+ # Base on Gizwits Snoti API, it would be one message which detected by '\n'.
84
+ # So sometimes it needs to get the message via length = 1 when the app's data flow is not large.
85
+ def smart_read
86
+ nbytes = 100
87
+ receive_total_by_one_byte = 0
88
+ begin
89
+ loop do
90
+ if !@exited
91
+ handle_snoti_data(@socket.read(nbytes))
92
+ (receive_total_by_one_byte += 1) if (nbytes == 1)
93
+ (nbytes, receive_total_by_one_byte = 100, 0) if (receive_total_by_one_byte >= 100)
94
+ else
95
+ break
96
+ end
97
+ end
98
+ rescue ConnTimeoutError, ReadTimeoutError => timeout_ex
99
+ nbytes, receive_total_by_one_byte = 1, 0
100
+ retry
101
+ rescue Exception => ex
102
+ raise ex
103
+ end
104
+ end
105
+
106
+ def handle_snoti_data(noti_data)
107
+ json_data = JSON.parse(noti_data)
108
+
109
+ case json_data["cmd"]
110
+ when "event_push"
111
+ @socket.ack(json_data['delivery_id'])
112
+ @event_push.call(json_data)
113
+ when "remote_control_res"
114
+ @remote_ctrl_res.call(json_data)
115
+ when "invalid_msg"
116
+ @error_res.call(json_data)
117
+ when "pong"
118
+ # Nothing to do
119
+ end
120
+ end
121
+
122
+ # Keep heartbeat to Gizwits Snoti API every 60 seconds in backgroud thread
123
+ def heartbeat
124
+ if @heartbeat_thread.nil?
125
+ @heartbeat_thread = every(60) do
126
+ begin
127
+ @socket.ping if !@exited
128
+ rescue Exception => e
129
+ @logger.error("[Heartbeat Error] ====> #{e}")
130
+ end
131
+
132
+ # If it catch system exit signal, return true to break the loop
133
+ is_break = @exited
134
+ is_break
135
+ end
136
+ end
137
+ end
138
+
139
+ # Handle remote control request to Gizwits Snoti API in backgroud thread
140
+ def invoke_remote_ctrl
141
+ if @remote_ctrl_thread.nil?
142
+ @remote_ctrl_thread = loop_after(2) do
143
+ if !@exited
144
+ begin
145
+ r_ctrl_req = @remote_ctrl.call
146
+ if r_ctrl_req.nil?
147
+ sleep 1
148
+ else
149
+ @logger.info("[Remote Control] ====> #{r_ctrl_req}")
150
+ @socket.remote_control(r_ctrl_req)
151
+ end
152
+ rescue Exception => e
153
+ @logger.error("[Remote Control Error] =====> #{e}")
154
+ end
155
+ end
156
+
157
+ # If it catch system exit signal, return true to break the loop
158
+ is_break = @exited
159
+ is_break
160
+ end
161
+ end
162
+ end
163
+
164
+ def every(interval)
165
+ Thread.new { loop { sleep interval; break if yield } }
166
+ end
167
+
168
+ def loop_after(seconds)
169
+ Thread.new { sleep seconds; loop { break if yield } }
170
+ end
171
+
172
+ def dispose
173
+ if @remote_ctrl_thread.nil?
174
+ @remote_ctrl_thread.kill
175
+ @remote_ctrl_thread = nil
176
+ end
177
+
178
+ if @heartbeat_thread.nil?
179
+ @heartbeat_thread.kill
180
+ @heartbeat_thread = nil
181
+ end
182
+ end
183
+
184
+ def listen_shut_down
185
+ # Trap ^C
186
+ Signal.trap('INT') { @exited = true }
187
+ # Trap `Kill`
188
+ Signal.trap('TERM') { @exited = true }
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,172 @@
1
+ require "socket"
2
+ require "openssl"
3
+ require "json"
4
+
5
+ # GizwitsSac = Gizwits Snoti API Client
6
+ module GizwitsSac
7
+ # options = {
8
+ # host: "xxx",
9
+ # port: "xxx",
10
+ # connect_timeout: xx,
11
+ # read_timeout: xx,
12
+ # write_timeout: xx,
13
+ # prefetch_count: xx,
14
+ # auth_data: [
15
+ # {
16
+ # product_key: "xxxx",
17
+ # auth_id: "xxxx",
18
+ # auth_secret: "xxxx",
19
+ # subkey: "xxxx",
20
+ # events: ['xxx', 'xxx']
21
+ # }
22
+ # ]
23
+ # }
24
+ class SnotiSocket
25
+ def initialize(options)
26
+ @host = options[:host]
27
+ @port = options[:port]
28
+ @timeout = options[:connect_timeout] || 3
29
+ @read_timeout = options[:read_timeout] || @timeout
30
+ @write_timeout = options[:write_timeout] || @timeout
31
+ @prefetch_count = options[:prefetch_count] || 50
32
+ @auth_data = options[:auth_data]
33
+ @socket = nil
34
+ @ssl_socket = nil
35
+ @lf = "\n"
36
+ @buffer = "".dup
37
+ end
38
+
39
+ def connect
40
+ socket_connect
41
+ ssl_connect
42
+ end
43
+
44
+ def read(n_bytes = 1)
45
+ index = nil
46
+ while (index = @buffer.index(@lf)) == nil
47
+ @buffer << read_data(n_bytes)
48
+ end
49
+ @buffer.slice!(0, index + @lf.bytesize)
50
+ end
51
+
52
+ def write(data)
53
+ write_data(data)
54
+ end
55
+
56
+ def closed?
57
+ @socket.nil? && @ssl_socket.nil?
58
+ end
59
+
60
+ def login_ok?
61
+ req_msg = "{\"cmd\":\"login_req\",\"prefetch_count\": #{@prefetch_count},\"data\": #{@auth_data.to_json}}"
62
+ write(req_msg)
63
+ resp =JSON.parse(read)
64
+ succeed = resp["data"]["result"]
65
+ # close connection if login failed
66
+ close if !succeed
67
+ return succeed
68
+ end
69
+
70
+ def remote_control(data_arr, msg_id = nil)
71
+ req = { cmd: "remote_control_req", data: data_arr }
72
+ req[:msg_id] = msg_id if !msg_id.nil?
73
+ write(req.to_json)
74
+ end
75
+
76
+ def ping
77
+ write("{\"cmd\": \"ping\"}")
78
+ end
79
+
80
+ def ack(delivery_id)
81
+ write("{\"cmd\": \"event_ack\",\"delivery_id\": #{delivery_id}}")
82
+ end
83
+
84
+ def close
85
+ @ssl_socket.close if (!@ssl_socket.nil? && !@ssl_socket.closed?)
86
+ @ssl_socket = nil
87
+ @socket.close if (!@socket.nil? && !@socket.closed?)
88
+ @socket = nil
89
+ end
90
+
91
+ private
92
+
93
+ def socket_connect
94
+ addr = Socket.getaddrinfo(@host, nil)
95
+ socket_addr = Socket.pack_sockaddr_in(@port, addr[0][3])
96
+
97
+ @socket = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0).tap do |socket|
98
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
99
+
100
+ begin
101
+ begin
102
+ non_blocking(socket, build_deadline(@timeout)) { socket.connect_nonblock(socket_addr) }
103
+ rescue Errno::EISCONN
104
+ # TODO: nothing to do
105
+ rescue ConnTimeoutError
106
+ raise ConnTimeoutError
107
+ end
108
+ rescue ConnTimeoutError => timeout_ex
109
+ raise ConnTimeoutError
110
+ rescue SystemCallError, IOError => exception
111
+ raise ConnFailedError.new("socket connection failed with exception: #{exception}")
112
+ end
113
+ end
114
+ end
115
+
116
+ def non_blocking(socket, deadline)
117
+ yield
118
+ rescue IO::WaitReadable
119
+ timeout = check_deadline(deadline)
120
+ raise ConnTimeoutError unless IO.select([socket], nil, nil, timeout)
121
+ retry
122
+ rescue IO::WaitWritable
123
+ timeout = check_deadline(deadline)
124
+ raise ConnTimeoutError unless IO.select(nil, [socket], nil, timeout)
125
+ retry
126
+ end
127
+
128
+ def check_deadline(deadline)
129
+ remaining = deadline - Time.now.utc
130
+ raise ConnTimeoutError if remaining < 0
131
+ return remaining
132
+ end
133
+
134
+ def build_deadline(timeout)
135
+ Time.now.utc + timeout
136
+ end
137
+
138
+ def ssl_connect
139
+ ssl_context = OpenSSL::SSL::SSLContext.new
140
+ ssl_context.set_params({verify_mode: OpenSSL::SSL::VERIFY_NONE})
141
+
142
+ @ssl_socket = OpenSSL::SSL::SSLSocket.new(@socket, ssl_context)
143
+ @ssl_socket.sync_close = true
144
+
145
+ ssl_socket_connect("ssl connection failed.", @timeout) {@ssl_socket.connect_nonblock}
146
+ end
147
+
148
+ def ssl_socket_connect(failed_desc, timeout)
149
+ begin
150
+ begin
151
+ non_blocking(@ssl_socket, build_deadline(timeout)) { yield }
152
+ rescue Errno::EISCONN
153
+ # TODO: nothing to do
154
+ rescue ConnTimeoutError
155
+ raise ConnTimeoutError
156
+ end
157
+ rescue ConnTimeoutError => timeout_ex
158
+ raise ConnTimeoutError
159
+ rescue SystemCallError, OpenSSL::SSL::SSLError, IOError => exception
160
+ raise ConnFailedError.new("#{failed_desc}\n Exception trace: #{exception}")
161
+ end
162
+ end
163
+
164
+ def write_data(data)
165
+ ssl_socket_connect("write data failed.", @write_timeout) {@ssl_socket.write_nonblock("#{data}\n")}
166
+ end
167
+
168
+ def read_data(n_bytes)
169
+ ssl_socket_connect("read data failed.", @read_timeout) {@ssl_socket.read_nonblock(n_bytes)}
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,3 @@
1
+ module GizwitsSac
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "gizwits_sac/version"
2
+ require "gizwits_sac/error"
3
+ require "gizwits_sac/snoti_socket"
4
+ require "gizwits_sac/snoti_client"
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gizwits_sac
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Abel Lai
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-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.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
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: pry
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
+ - !ruby/object:Gem::Dependency
70
+ name: pry-nav
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Gizwits Snoti API Client for Ruby.
84
+ email:
85
+ - yilaqingfeng@gamil.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE
94
+ - README.md
95
+ - README.zh_cn.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - gizwits_sac.gemspec
100
+ - lib/gizwits_sac.rb
101
+ - lib/gizwits_sac/error.rb
102
+ - lib/gizwits_sac/snoti_client.rb
103
+ - lib/gizwits_sac/snoti_socket.rb
104
+ - lib/gizwits_sac/version.rb
105
+ homepage: https://github.com/AbelLai/gizwits_sac_rb
106
+ licenses: []
107
+ metadata:
108
+ allowed_push_host: https://rubygems.org
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.5.1
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Gizwits Snoti API Client
129
+ test_files: []