tp_link 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ecbd0295f05df003c78bbcc8cf1299dc1a7af18
4
+ data.tar.gz: f34c62675f5fdd65b9607f06408d0c5a66f1c90a
5
+ SHA512:
6
+ metadata.gz: 9aa8132ae642b60c8bd28623acb3dbd3c5ab1e76d0c712fba47e5026b34cb838eaae53b49cbd58b916c18a9014cb113c2a53cccd8ded999630db4d17e8b08afd
7
+ data.tar.gz: 6b13493b9a882282f9fe0c9ec7015bd5204007813b1c3397289b0aa46f4a5805ab3d11d93fb710919ebe57ef3cb878bb9e097c9e8d06a098f6b9b066849e9643
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'faraday'
5
+ require 'net/https'
6
+ require 'json'
7
+ require 'faraday_middleware'
8
+ require 'yaml'
9
+ require 'json'
10
+ require 'securerandom'
11
+
12
+ require 'tp_link/errors.rb'
13
+ require 'tp_link/config.rb'
14
+ require 'tp_link/device.rb'
15
+ require 'tp_link/light.rb'
16
+ require 'tp_link/rgb_light.rb'
17
+ require 'tp_link/plug.rb'
18
+ require 'tp_link/api.rb'
19
+ require 'tp_link/smart_home.rb'
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TPLink Module
4
+ module TPLink
5
+ # Talk to TPLink's API. This class is use internally. You should not need to call it.
6
+ # @!visibility private
7
+ class API
8
+ # @!visibility private
9
+ DEFAULT_PARAMS = {
10
+ 'appName' => 'Kasa_Android',
11
+ 'termID' => proc { @config.terminal_uuid },
12
+ 'appVer' => '1.4.4.607',
13
+ 'ospf' => 'Android+6.0.1',
14
+ 'netType' => 'wifi',
15
+ 'locale' => 'es_ES'
16
+ }.freeze
17
+
18
+ # @!visibility private
19
+ DEFAULT_HEADERS = {
20
+ 'Connection' => 'Keep-Alive',
21
+ 'User-Agent' => 'Dalvik/2.1.0 (Linux; U; Android 6.0.1; ' \
22
+ 'A0001 Build/M4B30X)',
23
+ 'Content-Type' => 'applicatoin/json;charset=utf-8',
24
+ 'Accept' => 'application/json, text/plain, */*'
25
+ }.freeze
26
+
27
+ # # @!visibility private
28
+ # TOKEN_API = Faraday.new('https://wap.tplinkcloud.com') do |c|
29
+ # c.request :json
30
+ # c.response :json, content_type: /\bjson$/i
31
+ # c.adapter Faraday.default_adapter
32
+ # c.params.merge!(DEFAULT_PARAMS)
33
+ # c.headers.merge!(DEFAULT_HEADERS)
34
+ # end
35
+
36
+ # @!visibility private
37
+ TPLINK_API = Faraday.new('https://wap.tplinkcloud.com') do |c|
38
+ c.request :json
39
+ c.response :json, content_type: /\bjson$/i
40
+ c.adapter Faraday.default_adapter
41
+ c.params.merge!(DEFAULT_PARAMS)
42
+ c.headers.merge!(DEFAULT_HEADERS)
43
+ end
44
+
45
+ # @!visibility private
46
+ def initialize(opts = nil)
47
+ options = opts || "#{ENV['HOME']}/.tp_link"
48
+ @config = Config.new(options)
49
+ @token = nil
50
+ @device_list = []
51
+ end
52
+
53
+ def token(regen = false)
54
+ return @token if @token && regen == false
55
+ response = TPLINK_API.post do |req|
56
+ req.body = { method: 'login', url: 'https://wap.tplinkcloud.com',
57
+ params: @config.to_hash }.to_json
58
+ end
59
+ @token = parse_response(response)['token']
60
+ end
61
+
62
+ def device_list
63
+ @device_list if @device_list
64
+ response = TPLINK_API.post do |req|
65
+ req.params['token'] = token
66
+ req.body = { method: 'getDeviceList' }.to_json
67
+ end
68
+ @device_list = parse_response(response)['deviceList']
69
+ end
70
+
71
+ def send_data(device, data)
72
+ conn = data_connection(device)
73
+ res = conn.post do |req|
74
+ req.body = { method: 'passthrough',
75
+ params: { deviceId: device.id,
76
+ requestData: data.to_json } }.to_json
77
+ end
78
+ parse_response(res)
79
+ end
80
+
81
+ private
82
+
83
+ def data_connection(device)
84
+ Faraday.new(device.url) do |c|
85
+ c.request :json
86
+ c.response :json, content_type: /\bjson$/i
87
+ c.adapter Faraday.default_adapter
88
+ c.params['token'] = token
89
+ c.headers.merge!(DEFAULT_HEADERS)
90
+ end
91
+ end
92
+
93
+ def parse_response(res)
94
+ raise TPLink::TPLinkCloudError, 'Generic TPLinkCloud Error' unless res.success?
95
+ response = JSON.parse(res.body)
96
+ raise TPLink::DeviceOffline if response['error_code'].to_i == -20_571
97
+ raise TPLink::TPLinkCloudError, 'TPLinkCloud API Error' \
98
+ unless response['error_code'].to_i.zero?
99
+ raise TPLink::TPLinkCloudError, 'No respone data' \
100
+ unless res.body['result']
101
+ if response['result'].key?('responseData') && \
102
+ response['result']['responseData'].class == String
103
+ response['result']['responseData'] = \
104
+ JSON.parse(response['result']['responseData'])
105
+ end
106
+ response['result']
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TPLink
4
+ # This class handles the configuration.
5
+ # It is used internally and should not need to be called.
6
+ # @!visibility private
7
+ class Config
8
+ def initialize(config = "#{ENV['HOME']}/.tp_link")
9
+ @config_file = nil
10
+ @config = get_config(config)
11
+ raise 'User name not spcified in configuration.' \
12
+ unless @config.key?('user')
13
+ raise 'Password not specified in configuration.' \
14
+ unless @config.key?('password')
15
+ generate_uuid unless @config.key? 'uuid'
16
+ end
17
+
18
+ def generate_uuid
19
+ if @config_file && @config['uuid'].nil?
20
+ @config['uuid'] ||= SecureRandom.uuid
21
+ File.open(@config_file, 'w') do |f|
22
+ f.write @config.to_yaml
23
+ end
24
+ end
25
+ @config['uuid']
26
+ end
27
+
28
+ def app_type
29
+ 'Kasa_Android'
30
+ end
31
+
32
+ def terminal_uuid
33
+ @config['uuid']
34
+ end
35
+
36
+ def cloud_user_name
37
+ @config['user']
38
+ end
39
+
40
+ def cloud_password
41
+ @config['password']
42
+ end
43
+
44
+ def to_hash
45
+ {
46
+ appType: app_type,
47
+ cloudUserName: cloud_user_name,
48
+ cloudPassword: cloud_password,
49
+ terminalUUID: terminal_uuid
50
+ }
51
+ end
52
+
53
+ def to_json(_o)
54
+ to_hash.to_json
55
+ end
56
+
57
+ private
58
+
59
+ def get_config(config)
60
+ if config.is_a? String
61
+ raise "Config file missing: #{config}" \
62
+ unless File.exist?(config)
63
+ @config_file = config
64
+ return YAML.load_file(config) || {}
65
+ elsif !config.is_a?(Hash)
66
+ raise "Invalid config type #{config.class}"
67
+ end
68
+ config
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TPLink
4
+ # Generic class for all TPLink devices0
5
+ class Device
6
+ # Returns satus of device.
7
+ # @return [Boolean<1>] if device is on.
8
+ # @return [Boolean<0>] if device is off.
9
+ attr_reader :status
10
+
11
+ # Returns alais of device.
12
+ # @return [String] Device alias (name in kasa app).
13
+ attr_reader :alias
14
+
15
+ # Returns name of device.
16
+ # @return [String] Name of device (e.g. TP Link Smart Plug).
17
+ attr_reader :name
18
+
19
+ # @!visibility private
20
+ attr_reader :type, :url, :model, :mac, :role
21
+ # @!visibility private
22
+ attr_reader :same_region, :hw_id, :fw_id, :id, :hw_version, :fw_version
23
+
24
+ # @!visibility private
25
+ # This should only be called internally
26
+ def initialize(parent, dev)
27
+ @parent = parent
28
+ @alias = dev['alias']
29
+ @name = dev['deviceName']
30
+ @status = dev['status']
31
+ @type = dev['deviceType']
32
+ @url = dev['appServerUrl']
33
+ @model = dev['deviceModel']
34
+ @mac = dev['deviceMac']
35
+ @role = dev['role']
36
+ @same_region = dev['isSameRegion']
37
+ @hw_id = dev['hwId']
38
+ @fw_id = dev['fwId']
39
+ @id = dev['deviceId']
40
+ @hw_version = dev['deviceHwVersion']
41
+ @fw_version = dev['fwVer']
42
+ end
43
+
44
+ # Get Wifi signal strength in dB
45
+ # @returns [Integer]
46
+ def rssi
47
+ reload
48
+ @rssi
49
+ end
50
+
51
+ # Reload data / device state
52
+ def reload
53
+ res = @parent.send_data(self,
54
+ "system":
55
+ { "get_sysinfo": nil },
56
+ "emeter": { "get_realtime": nil })
57
+ @rssi = res['responseData']['system']['get_sysinfo']['rssi']
58
+ case self.class.to_s
59
+ when 'TPLink::Light'
60
+ reload_light(res)
61
+ when 'TPLink::Plug'
62
+ reload_plug(res)
63
+ end
64
+ true
65
+ end
66
+
67
+ # Turn device on
68
+ def on; end
69
+
70
+ # Turn device off
71
+ def off; end
72
+
73
+ # @return [True] if device is on.
74
+ # @return [False] if device is off.
75
+ def on?
76
+ reload
77
+ @status == 1
78
+ end
79
+
80
+ # @return [True] if device is off.
81
+ # @return [False] if device is on.
82
+ def off?
83
+ !on?
84
+ end
85
+
86
+ private
87
+
88
+ def reload_plug(res)
89
+ @status = res['responseData']['system']['get_sysinfo']['relay_state']
90
+ end
91
+
92
+ def reload_light(res)
93
+ @status = res['responseData']['system']['get_sysinfo']['light_state']['on_off']
94
+ end
95
+
96
+ def reload_rgb(res)
97
+ # TODO: Add variables for Hue, and Saturation
98
+ @status = res['responseData']['system']['get_sysinfo']['light_state']['on_off']
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Provides errors raised by the gem.
4
+
5
+ module TPLink
6
+ # Handle Errrors related to TPLink's Cloud
7
+ class DeviceOffline < StandardError
8
+ def initialize
9
+ super('Device Offline')
10
+ end
11
+ end
12
+ class TPLinkCloudError < StandardError
13
+ attr_reader :action
14
+ def initialize(message, action = nil)
15
+ super(message)
16
+ @action = action
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TPLink
4
+ # Control TPLink Dimmable lights
5
+ # @example
6
+ # light.on # turn on light
7
+ # light.off # turn off light
8
+ #
9
+ # # turn on light and set brightness to 50%
10
+ # light.on
11
+ # light.on(50)
12
+ class Light < TPLink::Device
13
+ # Turn light on
14
+ # @param b [Integer<1-100>] Set light intensity between 1 and 100
15
+ def on(b = 100)
16
+ transition_light_state(1, b)
17
+ end
18
+
19
+ # Turn light off
20
+ def off
21
+ transition_light_state(0, 100)
22
+ end
23
+
24
+ # Toggle device (turn off if on, on if off)
25
+ def toggle
26
+ if on?
27
+ off
28
+ else
29
+ on
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def transition_light_state(o, b)
36
+ @parent.send_data(self,
37
+ "smartlife.iot.smartbulb.lightingservice": {
38
+ "transition_light_state": {
39
+ "on_off": o,
40
+ "brightness": b
41
+ }
42
+ })
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TPLink
4
+ # Control TPLink Smart Plugs
5
+ class Plug < TPLink::Device
6
+ # Turn device on
7
+ def on
8
+ relay_state(1)
9
+ end
10
+
11
+ # Turn device off
12
+ def off
13
+ relay_state(0)
14
+ end
15
+
16
+ # Toggle device (turn off if on, on if off)
17
+ def toggle
18
+ if on?
19
+ off
20
+ else
21
+ on
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def relay_state(s)
28
+ @parent.send_data(self, "system": { "set_relay_state": { "state": s } })
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TPLink
4
+ # Control TP-Link LS130 / RGB lights
5
+ class RGBLight < TPLink::Device
6
+ # Turn Light on
7
+ def on(b = 100, h = 100, s = 100)
8
+ transition_light_state(1, b, h, s)
9
+ end
10
+
11
+ # Turn light off
12
+ def off
13
+ transition_light_state(0, 100, 100, 100)
14
+ end
15
+
16
+ # Toggle device (turn off if on, on if off)
17
+ def toggle
18
+ if on?
19
+ off
20
+ else
21
+ on
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def transition_light_state(o, b, h, s)
28
+ @parent.send_data(self,
29
+ "smartlife.iot.smartbulb.lightingservice": {
30
+ "transition_light_state": {
31
+ "on_off": o,
32
+ "brightness": b,
33
+ "hue": h,
34
+ "saturation": s
35
+ }
36
+ })
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TPLink
4
+ # Main class for TPLink. This is likely the only class you will initialize.
5
+ # @example
6
+ # sh = TPLink::SmartHome.new
7
+ #
8
+ # # Get array of TPLink Devices (currently only dimmable lights work).
9
+ # sh.devices
10
+ #
11
+ # # Find a device by name:
12
+ # light = sh.find("kitchen")
13
+ #
14
+ # # Turn light on
15
+ # light.on
16
+ #
17
+ # # Turn light off
18
+ # light.off
19
+ #
20
+ # # Dim light to 50%
21
+ # light.on(50)
22
+ class SmartHome
23
+ # @!visibility private
24
+ attr_reader :api
25
+ # @param [Hash,String] config options for TPLink
26
+ # @option config [String] :user Your Kasa user name
27
+ # @option config [String] :password Your Kasa password
28
+ # @option config [String] :uuid Your "device" (program) uuid.
29
+ # @example
30
+ # smarthome = TPLink::SmartHome.new(user: kasa@example.com, password: password: 1234)
31
+ # kitchen_light = smarthome.find("kitchen")
32
+ # kitchen_light.on
33
+ # kitchen_light.off
34
+ def initialize(config = {})
35
+ @api = TPLink::API.new(config)
36
+ reload
37
+ end
38
+
39
+ # Find a device by it's alias. Search is case insensitive.
40
+ # @param [String] a device alias.
41
+ # @return [TPLink::Light] If device is a dimmable light.
42
+ # @return [TPLink::RGBLight] if device is an RGB light.
43
+ # @return [TPLink::Plug] if device is a smart plug.
44
+ # @return [nil] if no device is found.
45
+ # @example Find your kitchen light
46
+ # smarthome.find("kitchen")
47
+ def find(a)
48
+ devices.find { |d| d.alias.match(/^#{a}$/i) }
49
+ end
50
+
51
+ # Find a device by it's alias.
52
+ # @return [Array<TPLink::Light,TPLink::RGBLight,TPLlink::Plug>] an array of devices.
53
+ # @example Turn everything off
54
+ # smarthome.devices.each { |device| device.off }
55
+ def devices
56
+ @devices ||= @raw_devices.map { |d| dev_to_class(d) }.compact
57
+ end
58
+
59
+ def send_data(device, data)
60
+ @api.send_data(device, data)
61
+ end
62
+
63
+ # Reload devices from TPLink api.
64
+ def reload
65
+ @raw_devices = @api.device_list
66
+ @devices = nil
67
+ end
68
+
69
+ private
70
+
71
+ # TODO: Test LB130, LB120, LB110, and BR30 devices.
72
+ def dev_to_class(device)
73
+ case device['deviceType']
74
+ when 'IOT.SMARTBULB'
75
+ return TPLink::Light.new(self, device) \
76
+ if device['deviceModel'].match(/^(LB100|LB110|BR30)/)
77
+ return TPLink::RGBLight.new(self, device) \
78
+ if device['deviceModel'].match(/^(LB130|LB120)/)
79
+ when 'IOT.SMARTPLUGSWITCH'
80
+ return TPLink::Plug.new(self, device)
81
+ end
82
+ nil
83
+ end
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tp_link
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - James Paterni
8
+ - Ian MOrgan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-03-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.13'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.13.1
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '0.13'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.13.1
34
+ - !ruby/object:Gem::Dependency
35
+ name: faraday_middleware
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.12'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.12.2
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '0.12'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.12.2
54
+ description: Control various TPLink smart home devices.
55
+ email: tp_link@ruby-code.com
56
+ executables: []
57
+ extensions: []
58
+ extra_rdoc_files: []
59
+ files:
60
+ - "./lib/tp_link.rb"
61
+ - "./lib/tp_link/api.rb"
62
+ - "./lib/tp_link/config.rb"
63
+ - "./lib/tp_link/device.rb"
64
+ - "./lib/tp_link/errors.rb"
65
+ - "./lib/tp_link/light.rb"
66
+ - "./lib/tp_link/plug.rb"
67
+ - "./lib/tp_link/rgb_light.rb"
68
+ - "./lib/tp_link/smart_home.rb"
69
+ homepage: https://ruby-code.com/james/tp_link
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.6.14
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: TPLink smarthome library
93
+ test_files: []