gizwits_sac 0.1.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: 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: []