tp_link_smartplug 0.0.3
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 +7 -0
- data/lib/tp_link_smartplug/command.rb +18 -0
- data/lib/tp_link_smartplug/device.rb +44 -0
- data/lib/tp_link_smartplug/helpers.rb +13 -0
- data/lib/tp_link_smartplug/message.rb +68 -0
- data/lib/tp_link_smartplug/message_helpers.rb +31 -0
- data/lib/tp_link_smartplug/version.rb +5 -0
- data/lib/tp_link_smartplug.rb +8 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f53d7ea800a82c44bc660a56066f5e7ee81d2ff2f786ddcb668c8d3267b8960c
|
4
|
+
data.tar.gz: af92b7cd428a8a3e1ceafb1868b3651153b5b192d051f0266174b14d4bbfba67
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f406f2bc6b7a7ebb0d0e8434146593a6080f4a2840d4420f96b3fd9454f31645adbfb8e248c5dab75e35c54ed9e72129b0e2355b508949271f1aa75371eb7269
|
7
|
+
data.tar.gz: 1b21d836bac38def6558452bd6fa6bbb9a4c0fe5023f70abb07c91bdd6921d5f4bb6db7db44d52ecf47d07d7fd417c0a5d8b71e10ccf5832becf864abb90844d
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TpLinkSmartplug
|
4
|
+
module Command
|
5
|
+
INFO = '{"system":{"get_sysinfo":{}}}'
|
6
|
+
ON = '{"system":{"set_relay_state":{"state":1}}}'
|
7
|
+
OFF = '{"system":{"set_relay_state":{"state":0}}}'
|
8
|
+
CLOUDINFO = '{"cnCloud":{"get_info":{}}}'
|
9
|
+
WLANSCAN = '{"netif":{"get_scaninfo":{"refresh":0}}}'
|
10
|
+
TIME = '{"time":{"get_time":{}}}'
|
11
|
+
SCHEDULE = '{"schedule":{"get_rules":{}}}'
|
12
|
+
COUNTDOWN = '{"count_down":{"get_rules":{}}}'
|
13
|
+
ANTITHEFT = '{"anti_theft":{"get_rules":{}}}'
|
14
|
+
REBOOT = '{"system":{"reboot":{"delay":1}}}'
|
15
|
+
RESET = '{"system":{"reset":{"delay":1}}}'
|
16
|
+
ENERGY = '{"emeter":{"get_realtime":{}}}'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'ipaddr'
|
5
|
+
require 'json'
|
6
|
+
require 'tp_link_smartplug/message'
|
7
|
+
|
8
|
+
module TpLinkSmartplug
|
9
|
+
# Provides an interface to a plug
|
10
|
+
class Device
|
11
|
+
include TpLinkSmartplug::Message
|
12
|
+
|
13
|
+
attr_accessor :address
|
14
|
+
attr_accessor :timeout
|
15
|
+
attr_accessor :port
|
16
|
+
attr_accessor :debug
|
17
|
+
|
18
|
+
def initialize(address:, port: 9999)
|
19
|
+
@address = IPAddr.new(address, Socket::AF_INET)
|
20
|
+
@port = port
|
21
|
+
@timeout = 3
|
22
|
+
@debug = false
|
23
|
+
end
|
24
|
+
|
25
|
+
[
|
26
|
+
:info,
|
27
|
+
:on,
|
28
|
+
:off,
|
29
|
+
:cloudinfo,
|
30
|
+
:wlanscan,
|
31
|
+
:time,
|
32
|
+
:schedule,
|
33
|
+
:countdown,
|
34
|
+
:antitheft,
|
35
|
+
:reboot,
|
36
|
+
:reset,
|
37
|
+
:energy
|
38
|
+
].each do |method|
|
39
|
+
define_method method do
|
40
|
+
JSON.parse(poll(address: @address, port: @port, command: TpLinkSmartplug::Command.const_get(method.upcase), timeout: @timeout, debug: @debug))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module TpLinkSmartplug
|
6
|
+
# Generic helper methods
|
7
|
+
module Helpers
|
8
|
+
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)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'tp_link_smartplug/helpers'
|
5
|
+
require 'tp_link_smartplug/message_helpers'
|
6
|
+
|
7
|
+
module TpLinkSmartplug
|
8
|
+
# Provides methods to send and receive messages from plugs
|
9
|
+
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)
|
25
|
+
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
|
35
|
+
end
|
36
|
+
|
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
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def disconnect(socket:)
|
65
|
+
socket.close unless socket.closed? || socket.nil?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tp_link_smartplug/command'
|
4
|
+
require 'tp_link_smartplug/device'
|
5
|
+
require 'tp_link_smartplug/helpers'
|
6
|
+
require 'tp_link_smartplug/message_helpers'
|
7
|
+
require 'tp_link_smartplug/message'
|
8
|
+
require 'tp_link_smartplug/version'
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tp_link_smartplug
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Hughes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-06-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Control and retrieve data from a TP-Link HS100/110 (Metered) Smartplug
|
14
|
+
email: bmhughes@bmhughes.co.uk
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- "./lib/tp_link_smartplug.rb"
|
20
|
+
- "./lib/tp_link_smartplug/command.rb"
|
21
|
+
- "./lib/tp_link_smartplug/device.rb"
|
22
|
+
- "./lib/tp_link_smartplug/helpers.rb"
|
23
|
+
- "./lib/tp_link_smartplug/message.rb"
|
24
|
+
- "./lib/tp_link_smartplug/message_helpers.rb"
|
25
|
+
- "./lib/tp_link_smartplug/version.rb"
|
26
|
+
homepage: https://github.com/bmhughes/tp_link_smartplug
|
27
|
+
licenses:
|
28
|
+
- Apache-2.0
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '2.5'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.7.7
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: TP-Link HS100/110 Smart Plug interaction library
|
50
|
+
test_files: []
|