tp_link_smartplug 0.0.3 → 0.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f53d7ea800a82c44bc660a56066f5e7ee81d2ff2f786ddcb668c8d3267b8960c
4
- data.tar.gz: af92b7cd428a8a3e1ceafb1868b3651153b5b192d051f0266174b14d4bbfba67
3
+ metadata.gz: 23a1848523acc948dd653d39fa3b9cec937bdba9d2a0ad6970308bac4af64d8d
4
+ data.tar.gz: dd4d4a0fda92aeabfdc7adfb5f751f1b957438b168e5c96efaf4950d69e1bb3d
5
5
  SHA512:
6
- metadata.gz: f406f2bc6b7a7ebb0d0e8434146593a6080f4a2840d4420f96b3fd9454f31645adbfb8e248c5dab75e35c54ed9e72129b0e2355b508949271f1aa75371eb7269
7
- data.tar.gz: 1b21d836bac38def6558452bd6fa6bbb9a4c0fe5023f70abb07c91bdd6921d5f4bb6db7db44d52ecf47d07d7fd417c0a5d8b71e10ccf5832becf864abb90844d
6
+ metadata.gz: e2259524ac344b200b19d4824178dfe18b5b7d92c8fbead1b3a4c40f3c073a3078b529f73bf035c4e1505f9827c9323f2f5c8e98f8076270783ed701d1ce6058
7
+ data.tar.gz: 24ae9be641e069804c884abd18594ee8a2c355370033c06c36091dff990c0846325cc9878c1e08550658e88fabebe42e98ce60b0edf82cc57f71006b0b5d1753
@@ -1,18 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TpLinkSmartplug
4
+ # Module containing the static commands for the plug
4
5
  module Command
6
+ # Plug information command
5
7
  INFO = '{"system":{"get_sysinfo":{}}}'
8
+ # Plug output on command
6
9
  ON = '{"system":{"set_relay_state":{"state":1}}}'
10
+ # Plug output off command
7
11
  OFF = '{"system":{"set_relay_state":{"state":0}}}'
12
+ # Plug cloud info command
8
13
  CLOUDINFO = '{"cnCloud":{"get_info":{}}}'
14
+ # Plug WLAN SSID scan command
9
15
  WLANSCAN = '{"netif":{"get_scaninfo":{"refresh":0}}}'
16
+ # Plug time command
10
17
  TIME = '{"time":{"get_time":{}}}'
18
+ # Plug schedule command
11
19
  SCHEDULE = '{"schedule":{"get_rules":{}}}'
20
+ # Plug countdown command
12
21
  COUNTDOWN = '{"count_down":{"get_rules":{}}}'
22
+ # Plug antitheft command
13
23
  ANTITHEFT = '{"anti_theft":{"get_rules":{}}}'
24
+ # Plug reboot command
14
25
  REBOOT = '{"system":{"reboot":{"delay":1}}}'
26
+ # Plug reset command
15
27
  RESET = '{"system":{"reset":{"delay":1}}}'
28
+ # Plug energy command
16
29
  ENERGY = '{"emeter":{"get_realtime":{}}}'
17
30
  end
18
31
  end
@@ -3,16 +3,24 @@
3
3
  require 'socket'
4
4
  require 'ipaddr'
5
5
  require 'json'
6
+ require 'tp_link_smartplug/helpers'
6
7
  require 'tp_link_smartplug/message'
7
8
 
8
9
  module TpLinkSmartplug
9
- # Provides an interface to a plug
10
+ # Provides an object to represent to a plug
11
+ #
12
+ # @author Ben Hughes
13
+ # @attr [IPAddr] address IP address of the plug
14
+ # @attr [Integer] port Port to connect to on the plug
15
+ # @attr [Integer] timeout Timeout value for connecting and sending commands to the plug
16
+ # @attr [true, false] debug Control debug logging
10
17
  class Device
18
+ include TpLinkSmartplug::Helpers
11
19
  include TpLinkSmartplug::Message
12
20
 
13
21
  attr_accessor :address
14
- attr_accessor :timeout
15
22
  attr_accessor :port
23
+ attr_accessor :timeout
16
24
  attr_accessor :debug
17
25
 
18
26
  def initialize(address:, port: 9999)
@@ -20,8 +28,130 @@ module TpLinkSmartplug
20
28
  @port = port
21
29
  @timeout = 3
22
30
  @debug = false
31
+ @sockaddr = Addrinfo.getaddrinfo(address.to_s, port, Socket::PF_INET, :STREAM, 6).first.to_sockaddr
32
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
33
+ end
34
+
35
+ # Open connection to plug
36
+ #
37
+ def connect
38
+ debug_message("Connecting to #{@address} port #{@port}") if @debug
39
+ debug_message("Connecting, socket state: #{@socket.closed? ? 'closed' : 'open'}") if @debug
40
+
41
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM) if closed?
42
+
43
+ begin
44
+ @socket.connect_nonblock(@sockaddr)
45
+ rescue IO::WaitWritable
46
+ if IO.select(nil, [@socket], nil, timeout)
47
+ begin
48
+ @socket.connect_nonblock(@sockaddr)
49
+ rescue Errno::EISCONN
50
+ debug_message('Connected') if debug
51
+ rescue StandardError => e
52
+ disconnect
53
+ debug_message('Unexpected exception encountered.') if debug
54
+ raise e
55
+ end
56
+ else
57
+ @socket.close
58
+ raise "Connection timeout connecting to address #{@address}, port #{@port}."
59
+ end
60
+ rescue Errno::EISCONN
61
+ debug_message('Connected') if debug
62
+ end
63
+ end
64
+
65
+ # Close connection to plug
66
+ #
67
+ def disconnect
68
+ @socket.close unless @socket.closed?
69
+ end
70
+
71
+ # Return connection state
72
+ #
73
+ # @return [True, False]
74
+ def open?
75
+ !@socket.closed?
76
+ end
77
+
78
+ # Return connection state
79
+ #
80
+ # @return [True, False]
81
+ def closed?
82
+ @socket.closed?
83
+ end
84
+
85
+ # Polls a plug with a command
86
+ #
87
+ # @param command [String] the command to send to the plug
88
+ # @return [Hash] the output from the plug command
89
+ def poll(command:)
90
+ connect
91
+
92
+ begin
93
+ debug_message("Sending: #{decrypt(encrypt(command)[4..(command.length + 4)])}") if @debug
94
+ @socket.write_nonblock(encrypt(command))
95
+ rescue IO::WaitWritable
96
+ IO.select(nil, [@socket])
97
+ retry
98
+ end
99
+
100
+ begin
101
+ data = @socket.recv_nonblock(2048)
102
+ rescue IO::WaitReadable
103
+ IO.select([@socket])
104
+ retry
105
+ ensure
106
+ disconnect unless closed?
107
+ end
108
+
109
+ raise 'Error occured during disconnect' unless closed?
110
+ raise 'No data received' if data.nil? || data.empty?
111
+
112
+ debug_message("Received Raw: #{data}") if debug
113
+ data = decrypt(data[4..data.length])
114
+ debug_message("Received Decrypted: #{data}") if debug
115
+
116
+ data
23
117
  end
24
118
 
119
+ # @!method info
120
+ # Return plug information
121
+ # @return [Hash]
122
+ # @!method on
123
+ # Turn plug output on
124
+ # @return [Hash]
125
+ # @!method off
126
+ # Turn plug output off
127
+ # @return [Hash]
128
+ # @!method cloudinfo
129
+ # Return plug cloud account configuration
130
+ # @return [Hash]
131
+ # @!method wlanscan
132
+ # Perform a scan for wireless SSIDs
133
+ # @return [Hash]
134
+ # @!method time
135
+ # Return system time from the plug
136
+ # @return [Hash]
137
+ # @!method schedule
138
+ # Return schedule configured on the plug
139
+ # @return [Hash]
140
+ # @!method countdown
141
+ # Return countdown configured on the plug
142
+ # @return [Hash]
143
+ # @!method antitheft
144
+ # Unsure
145
+ # @return [Hash]
146
+ # @!method reboot
147
+ # Reboot plug
148
+ # @return [Hash]
149
+ # @!method resry
150
+ # Reset plug
151
+ # @return [Hash]
152
+ # @!method energy
153
+ # Return plug energy data
154
+ # @return [Hash]
25
155
  [
26
156
  :info,
27
157
  :on,
@@ -37,7 +167,7 @@ module TpLinkSmartplug
37
167
  :energy
38
168
  ].each do |method|
39
169
  define_method method do
40
- JSON.parse(poll(address: @address, port: @port, command: TpLinkSmartplug::Command.const_get(method.upcase), timeout: @timeout, debug: @debug))
170
+ JSON.parse(poll(command: TpLinkSmartplug::Command.const_get(method.upcase)))
41
171
  end
42
172
  end
43
173
  end
@@ -5,9 +5,12 @@ require 'time'
5
5
  module TpLinkSmartplug
6
6
  # Generic helper methods
7
7
  module Helpers
8
+ # Formats a message for output as a debug log
9
+ #
10
+ # @param string [String] the message to be formatted for debug output
8
11
  def debug_message(string)
9
- caller_method = caller_locations(1..1).label
10
- Time.now.strftime('%Y-%m-%d %H:%M:%S: ').concat("#{caller_method}: ").concat(string)
12
+ caller_method = caller_locations(1..1).first.label
13
+ STDOUT.puts(Time.now.strftime('%Y-%m-%d %H:%M:%S: ').concat("#{caller_method}: ").concat(string))
11
14
  end
12
15
  end
13
16
  end
@@ -1,68 +1,33 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
- require 'tp_link_smartplug/helpers'
5
- require 'tp_link_smartplug/message_helpers'
1
+ # frozen_string_literal: false
6
2
 
7
3
  module TpLinkSmartplug
8
- # Provides methods to send and receive messages from plugs
4
+ # Helper methods for plug communication messages
9
5
  module Message
10
- include TpLinkSmartplug::Helpers
11
- include TpLinkSmartplug::MessageHelpers
12
-
13
- def poll(address:, port:, command:, timeout: 3, debug: false)
14
- socket = connect(address: address, port: port, timeout: timeout, debug: debug)
15
- STDOUT.puts(debug_message("Sending: #{decrypt(encrypt(command)[4..command.length])}")) if debug
16
-
17
- begin
18
- socket.write_nonblock(encrypt(command))
19
- data = socket.recv_nonblock(2048)
20
- rescue IO::WaitReadable, IO::WaitWritable
21
- IO.select([socket], [socket])
22
- retry
23
- ensure
24
- disconnect(socket: socket)
6
+ # Encrypts a message to send to the smart plug
7
+ #
8
+ # @param string [String] the message to be encrypted
9
+ def encrypt(string)
10
+ key = 171
11
+ result = [string.length].pack('N')
12
+ string.each_char do |char|
13
+ key = a = key ^ char.ord
14
+ result.concat(a.chr)
25
15
  end
26
-
27
- raise 'Error occured during disconnect' unless socket.closed?
28
- raise 'No data received' if data.nil? || data.empty?
29
-
30
- STDOUT.puts(debug_message("Received Raw: #{data}")) if debug
31
- data = decrypt(data[4..data.length])
32
- STDOUT.puts(debug_message("Received: #{data}")) if debug
33
-
34
- data
16
+ result
35
17
  end
36
18
 
37
- private
38
-
39
- def connect(address:, port:, timeout: 3, debug: false)
40
- STDOUT.puts(debug_message("Connecting to #{address} port #{port}")) if debug
41
-
42
- Socket.new(Socket::AF_INET, Socket::SOCK_STREAM).tap do |socket|
43
- sockaddr = Addrinfo.getaddrinfo(address.to_s, port, Socket::PF_INET, :STREAM, 6).first.to_sockaddr
44
- STDOUT.puts(debug_message("Connecting, socket closed: #{socket.closed?}")) if debug
45
- socket.connect_nonblock(sockaddr)
46
- rescue IO::WaitWritable
47
- if IO.select(nil, [socket], nil, timeout)
48
- begin
49
- socket.connect_nonblock(sockaddr)
50
- rescue Errno::EISCONN
51
- STDOUT.puts(debug_message('Connected')) if debug
52
- rescue StandardError => e
53
- socket.close
54
- STDOUT.puts(debug_message('Unexpected exception encountered.')) if debug
55
- raise e
56
- end
57
- else
58
- socket.close
59
- raise "Connection timeout connecting to address #{address}, port #{port}."
60
- end
19
+ # Decrypts a message received from the smart plug
20
+ #
21
+ # @param string [String] the message to be decrypted
22
+ def decrypt(string)
23
+ key = 171
24
+ result = ''
25
+ string.each_char do |char|
26
+ a = key ^ char.ord
27
+ key = char.ord
28
+ result.concat(a.chr)
61
29
  end
62
- end
63
-
64
- def disconnect(socket:)
65
- socket.close unless socket.closed? || socket.nil?
30
+ result
66
31
  end
67
32
  end
68
33
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # @author Ben Hughes
3
4
  module TpLinkSmartplug
5
+ # Gem version
4
6
  VERSION = '0.0.1'
5
7
  end
@@ -3,6 +3,5 @@
3
3
  require 'tp_link_smartplug/command'
4
4
  require 'tp_link_smartplug/device'
5
5
  require 'tp_link_smartplug/helpers'
6
- require 'tp_link_smartplug/message_helpers'
7
6
  require 'tp_link_smartplug/message'
8
7
  require 'tp_link_smartplug/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tp_link_smartplug
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Hughes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-07 00:00:00.000000000 Z
11
+ date: 2019-06-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Control and retrieve data from a TP-Link HS100/110 (Metered) Smartplug
14
14
  email: bmhughes@bmhughes.co.uk
@@ -21,7 +21,6 @@ files:
21
21
  - "./lib/tp_link_smartplug/device.rb"
22
22
  - "./lib/tp_link_smartplug/helpers.rb"
23
23
  - "./lib/tp_link_smartplug/message.rb"
24
- - "./lib/tp_link_smartplug/message_helpers.rb"
25
24
  - "./lib/tp_link_smartplug/version.rb"
26
25
  homepage: https://github.com/bmhughes/tp_link_smartplug
27
26
  licenses:
@@ -38,9 +37,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
37
  version: '2.5'
39
38
  required_rubygems_version: !ruby/object:Gem::Requirement
40
39
  requirements:
41
- - - ">="
40
+ - - ">"
42
41
  - !ruby/object:Gem::Version
43
- version: '0'
42
+ version: 1.3.1
44
43
  requirements: []
45
44
  rubyforge_project:
46
45
  rubygems_version: 2.7.7
@@ -1,31 +0,0 @@
1
- module TpLinkSmartplug
2
- # Helper methods for plug communication messages
3
- module MessageHelpers
4
- # Encrypts a message to send to the smart plug
5
- #
6
- # @string format [String] the message to be encrypted
7
- def encrypt(string)
8
- key = 171
9
- result = [string.length].pack('N')
10
- string.each_char do |char|
11
- key = a = key ^ char.ord
12
- result.concat(a.chr)
13
- end
14
- result
15
- end
16
-
17
- # Decrypts a message received from the smart plug
18
- #
19
- # @string format [String] the message to be decrypted
20
- def decrypt(string)
21
- key = 171
22
- result = ''
23
- string.each_char do |char|
24
- a = key ^ char.ord
25
- key = char.ord
26
- result.concat(a.chr)
27
- end
28
- result
29
- end
30
- end
31
- end