wizrb 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +62 -0
- data/doc/devices.md +135 -0
- data/doc/lighting.md +186 -0
- data/doc/power.md +37 -0
- data/exe/wizrb +5 -0
- data/lib/wizrb/cli.rb +79 -0
- data/lib/wizrb/lighting/discover.rb +40 -0
- data/lib/wizrb/lighting/events/set_brightness_event.rb +27 -0
- data/lib/wizrb/lighting/events/set_cold_white_event.rb +27 -0
- data/lib/wizrb/lighting/events/set_color_temp_event.rb +29 -0
- data/lib/wizrb/lighting/events/set_rgb_event.rb +29 -0
- data/lib/wizrb/lighting/events/set_scene_event.rb +22 -0
- data/lib/wizrb/lighting/events/set_speed_event.rb +27 -0
- data/lib/wizrb/lighting/events/set_warm_white_event.rb +27 -0
- data/lib/wizrb/lighting/group.rb +37 -0
- data/lib/wizrb/lighting/products/dimable_light.rb +33 -0
- data/lib/wizrb/lighting/products/light.rb +55 -0
- data/lib/wizrb/lighting/products/rgb_light.rb +58 -0
- data/lib/wizrb/lighting/products/tunable_light.rb +39 -0
- data/lib/wizrb/lighting/scenes/scene.rb +68 -0
- data/lib/wizrb/lighting/scenes/spooky_scene.rb +61 -0
- data/lib/wizrb/lighting/state.rb +45 -0
- data/lib/wizrb/lighting.rb +41 -0
- data/lib/wizrb/power/discover.rb +34 -0
- data/lib/wizrb/power/group.rb +10 -0
- data/lib/wizrb/power/products/smart_plug.rb +17 -0
- data/lib/wizrb/shared/connection.rb +76 -0
- data/lib/wizrb/shared/discover.rb +116 -0
- data/lib/wizrb/shared/events/base.rb +22 -0
- data/lib/wizrb/shared/events/power_event.rb +22 -0
- data/lib/wizrb/shared/events/reboot_event.rb +15 -0
- data/lib/wizrb/shared/events/refresh_event.rb +15 -0
- data/lib/wizrb/shared/events/reset_event.rb +15 -0
- data/lib/wizrb/shared/group.rb +68 -0
- data/lib/wizrb/shared/products/device.rb +105 -0
- data/lib/wizrb/shared/state.rb +32 -0
- data/lib/wizrb/version.rb +5 -0
- data/lib/wizrb.rb +32 -0
- metadata +45 -4
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../shared/events/base'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Events
|
8
|
+
class SetRgbEvent < Wizrb::Shared::Events::Base
|
9
|
+
MIN_VALUE = 1
|
10
|
+
MAX_VALUE = 255
|
11
|
+
|
12
|
+
def initialize(red, green, blue)
|
13
|
+
validate_color!('Red', red)
|
14
|
+
validate_color!('Green', green)
|
15
|
+
validate_color!('Blue', blue)
|
16
|
+
super(method: 'setState', params: { r: red, g: green, b: blue })
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_color!(color, value)
|
22
|
+
return if value && value >= MIN_VALUE && value <= MAX_VALUE
|
23
|
+
|
24
|
+
raise ArgumentError, "#{color} must be between #{MIN_VALUE} and #{MAX_VALUE}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../shared/events/base'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Events
|
8
|
+
class SetSceneEvent < Wizrb::Shared::Events::Base
|
9
|
+
def initialize(value)
|
10
|
+
validate!(value)
|
11
|
+
super(method: 'setState', params: { sceneId: Wizrb::Lighting::SCENES[value] })
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def validate!(value)
|
17
|
+
raise ArgumentError, 'Invalid scene' unless Wizrb::Lighting::SCENES.include?(value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../shared/events/base'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Events
|
8
|
+
class SetSpeedEvent < Wizrb::Shared::Events::Base
|
9
|
+
MIN_VALUE = 10
|
10
|
+
MAX_VALUE = 200
|
11
|
+
|
12
|
+
def initialize(value)
|
13
|
+
validate!(value)
|
14
|
+
super(method: 'setState', params: { speed: value })
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def validate!(value)
|
20
|
+
return if value && value >= MIN_VALUE && value <= MAX_VALUE
|
21
|
+
|
22
|
+
raise ArgumentError, "Speed must be between #{MIN_VALUE} and #{MAX_VALUE}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../shared/events/base'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Events
|
8
|
+
class SetWarmWhiteEvent < Wizrb::Shared::Events::Base
|
9
|
+
MIN_VALUE = 1
|
10
|
+
MAX_VALUE = 255
|
11
|
+
|
12
|
+
def initialize(value)
|
13
|
+
validate!(value)
|
14
|
+
super(method: 'setState', params: { w: value })
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def validate!(value)
|
20
|
+
return if value && value >= MIN_VALUE && value <= MAX_VALUE
|
21
|
+
|
22
|
+
raise ArgumentError, "Warm white must be between #{MIN_VALUE} and #{MAX_VALUE}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../shared/group'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
class Group < Wizrb::Shared::Group
|
8
|
+
def brightness(value)
|
9
|
+
dispatch_event(Wizrb::Lighting::Events::SetBrightnessEvent.new(value))
|
10
|
+
end
|
11
|
+
|
12
|
+
def cold_white(value)
|
13
|
+
dispatch_event(Wizrb::Lighting::Events::SetColdWhiteEvent.new(value))
|
14
|
+
end
|
15
|
+
|
16
|
+
def color_temp(value)
|
17
|
+
dispatch_event(Wizrb::Lighting::Events::SetColorTempEvent.new(value))
|
18
|
+
end
|
19
|
+
|
20
|
+
def rgb(red, green, blue)
|
21
|
+
dispatch_event(Wizrb::Lighting::Events::SetRgbEvent.new(red, green, blue))
|
22
|
+
end
|
23
|
+
|
24
|
+
def speed(value)
|
25
|
+
dispatch_event(Wizrb::Lighting::Events::SetSpeedEvent.new(value))
|
26
|
+
end
|
27
|
+
|
28
|
+
def warm_white(value)
|
29
|
+
dispatch_event(Wizrb::Lighting::Events::SetWarmWhiteEvent.new(value))
|
30
|
+
end
|
31
|
+
|
32
|
+
def scene(value)
|
33
|
+
dispatch_event(Wizrb::Lighting::Events::SetSceneEvent.new(value))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'light'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Products
|
8
|
+
class DimableLight < Wizrb::Lighting::Products::Light
|
9
|
+
MODULE_NAME_IDENTIFIER = 'DW'
|
10
|
+
FEATURES = {
|
11
|
+
brightness: true,
|
12
|
+
color_temp: false,
|
13
|
+
color: false,
|
14
|
+
effect: false,
|
15
|
+
scenes: %i[
|
16
|
+
wake_up
|
17
|
+
bedtime
|
18
|
+
cool_white
|
19
|
+
night_light
|
20
|
+
candlelight
|
21
|
+
golden_white
|
22
|
+
pulse
|
23
|
+
steampunk
|
24
|
+
]
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def initialize(ip:, port: 38_899)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'socket'
|
5
|
+
require_relative '../../shared/products/device'
|
6
|
+
require_relative '../state'
|
7
|
+
|
8
|
+
module Wizrb
|
9
|
+
module Lighting
|
10
|
+
module Products
|
11
|
+
class Light < Wizrb::Shared::Products::Device
|
12
|
+
def initialize(ip:, port: 38_899)
|
13
|
+
super(ip: ip, port: port, state: Wizrb::Lighting::State.new)
|
14
|
+
end
|
15
|
+
|
16
|
+
def white_range
|
17
|
+
@white_range ||=
|
18
|
+
user_config&.dig('whiteRange') ||
|
19
|
+
model_config&.dig('extRange') ||
|
20
|
+
model_config&.dig('cctRange') ||
|
21
|
+
user_config&.dig('extRange') ||
|
22
|
+
user_config&.dig('cctRange')
|
23
|
+
end
|
24
|
+
|
25
|
+
def brightness(value)
|
26
|
+
dispatch_event(Wizrb::Lighting::Events::SetBrightnessEvent.new(value))
|
27
|
+
end
|
28
|
+
|
29
|
+
def cold_white(value)
|
30
|
+
dispatch_event(Wizrb::Lighting::Events::SetColdWhiteEvent.new(value))
|
31
|
+
end
|
32
|
+
|
33
|
+
def color_temp(value)
|
34
|
+
dispatch_event(Wizrb::Lighting::Events::SetColorTempEvent.new(value))
|
35
|
+
end
|
36
|
+
|
37
|
+
def rgb(red, green, blue)
|
38
|
+
dispatch_event(Wizrb::Lighting::Events::SetRgbEvent.new(red, green, blue))
|
39
|
+
end
|
40
|
+
|
41
|
+
def speed(value)
|
42
|
+
dispatch_event(Wizrb::Lighting::Events::SetSpeedEvent.new(value))
|
43
|
+
end
|
44
|
+
|
45
|
+
def warm_white(value)
|
46
|
+
dispatch_event(Wizrb::Lighting::Events::SetWarmWhiteEvent.new(value))
|
47
|
+
end
|
48
|
+
|
49
|
+
def scene(value)
|
50
|
+
dispatch_event(Wizrb::Lighting::Events::SetSceneEvent.new(value))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'light'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Products
|
8
|
+
class RgbLight < Wizrb::Lighting::Products::Light
|
9
|
+
MODULE_NAME_IDENTIFIER = 'RGB'
|
10
|
+
FEATURES = {
|
11
|
+
brightness: true,
|
12
|
+
color_temp: true,
|
13
|
+
color: true,
|
14
|
+
effect: true,
|
15
|
+
scenes: %i[
|
16
|
+
ocean
|
17
|
+
romance
|
18
|
+
sunset
|
19
|
+
party
|
20
|
+
fireplace
|
21
|
+
cozy
|
22
|
+
forest
|
23
|
+
pastel_colors
|
24
|
+
wake_up
|
25
|
+
bedtime
|
26
|
+
warm_white
|
27
|
+
daylight
|
28
|
+
cool_white
|
29
|
+
night_light
|
30
|
+
focus
|
31
|
+
relax
|
32
|
+
true_colors
|
33
|
+
tv_time
|
34
|
+
plantgrowth
|
35
|
+
spring
|
36
|
+
summer
|
37
|
+
fall
|
38
|
+
deepdive
|
39
|
+
jungle
|
40
|
+
mojito
|
41
|
+
club
|
42
|
+
christmas
|
43
|
+
halloween
|
44
|
+
candlelight
|
45
|
+
golden_white
|
46
|
+
pulse
|
47
|
+
steampunk
|
48
|
+
rhythm
|
49
|
+
]
|
50
|
+
}.freeze
|
51
|
+
|
52
|
+
def initialize(ip:, port: 38_899)
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'light'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Products
|
8
|
+
class TunableLight < Wizrb::Lighting::Products::Light
|
9
|
+
MODULE_NAME_IDENTIFIER = 'TW'
|
10
|
+
FEATURES = {
|
11
|
+
brightness: true,
|
12
|
+
color_temp: true,
|
13
|
+
color: false,
|
14
|
+
effect: true,
|
15
|
+
scenes: %i[
|
16
|
+
cozy
|
17
|
+
wake_up
|
18
|
+
bedtime
|
19
|
+
warm_white
|
20
|
+
daylight
|
21
|
+
cool_white
|
22
|
+
night_light
|
23
|
+
focus
|
24
|
+
relax
|
25
|
+
tv_time
|
26
|
+
candlelight
|
27
|
+
golden_white
|
28
|
+
pulse
|
29
|
+
steampunk
|
30
|
+
]
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
def initialize(ip:, port: 38_899)
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../group'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Scenes
|
8
|
+
class Scene
|
9
|
+
def initialize(group, stop_delay: 1, save: true)
|
10
|
+
validate!(group)
|
11
|
+
@group = group
|
12
|
+
@stop_delay = stop_delay
|
13
|
+
@save = save
|
14
|
+
@running = false
|
15
|
+
@thread = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
@running = true
|
20
|
+
@thread = Thread.new do
|
21
|
+
save_state if @save
|
22
|
+
before_start
|
23
|
+
step while @running
|
24
|
+
after_stop
|
25
|
+
ensure
|
26
|
+
restore_state if @save
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
@running = false
|
32
|
+
sleep(@stop_delay)
|
33
|
+
@thread.terminate
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def before_start; end
|
39
|
+
|
40
|
+
def step
|
41
|
+
raise Wizrb::Error, 'You failed to implement #step on your scene'
|
42
|
+
end
|
43
|
+
|
44
|
+
def after_stop; end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def validate!(value)
|
49
|
+
return if value.is_a?(Wizrb::Lighting::Group)
|
50
|
+
|
51
|
+
raise Wizrb::Error, 'Scenes can only be activated on lighting groups'
|
52
|
+
end
|
53
|
+
|
54
|
+
def save_state
|
55
|
+
@state_events = @group.map do |device|
|
56
|
+
Wizrb::Shared::Events::Base.new(method: 'setState', params: device.refresh.state)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def restore_state
|
61
|
+
@group.each_with_index do |device, i|
|
62
|
+
device.dispatch_event(@state_events[i])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'scene'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Lighting
|
7
|
+
module Scenes
|
8
|
+
class SpookyScene < Wizrb::Lighting::Scenes::Scene
|
9
|
+
DEFAULT_STATE_EVENTS = [
|
10
|
+
Wizrb::Shared::Events::PowerEvent.new(true),
|
11
|
+
Wizrb::Lighting::Events::SetSpeedEvent.new(200),
|
12
|
+
Wizrb::Lighting::Events::SetColorTempEvent.new(1000),
|
13
|
+
Wizrb::Lighting::Events::SetBrightnessEvent.new(100)
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
BLACKOUT_EVENTS = [
|
17
|
+
Wizrb::Shared::Events::PowerEvent.new(false)
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def initialize(group, sync: false)
|
21
|
+
@sync = sync
|
22
|
+
super(group, stop_delay: 10)
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def before_start
|
28
|
+
dispatch(DEFAULT_STATE_EVENTS, sync: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
def step
|
32
|
+
if Random.rand > 0.5
|
33
|
+
Random.rand(1..10).times do
|
34
|
+
next if Random.rand > 0.25
|
35
|
+
|
36
|
+
dispatch(BLACKOUT_EVENTS, sync: @sync)
|
37
|
+
sleep(Random.rand > 0.98 ? 3 : 0.125)
|
38
|
+
dispatch(DEFAULT_STATE_EVENTS, sync: true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
sleep(Random.rand(0.125..5.0))
|
43
|
+
end
|
44
|
+
|
45
|
+
def after_stop
|
46
|
+
dispatch(DEFAULT_STATE_EVENTS, sync: true)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def dispatch(events, sync: false)
|
52
|
+
if sync || Random.rand > 0.5
|
53
|
+
@group.dispatch_events(*events)
|
54
|
+
else
|
55
|
+
@group.devices.sample.dispatch_events(*events)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wizrb
|
4
|
+
module Lighting
|
5
|
+
class State < Wizrb::Shared::State
|
6
|
+
STATE_KEYS = %i[state w c r g b temp dimming speed sceneId].freeze
|
7
|
+
|
8
|
+
def parse!(response)
|
9
|
+
result = response&.dig('result')
|
10
|
+
return unless result
|
11
|
+
|
12
|
+
@state = result.transform_keys(&:to_sym).slice(*STATE_KEYS)
|
13
|
+
@state.delete(:sceneId) if @state[:sceneId]&.zero?
|
14
|
+
end
|
15
|
+
|
16
|
+
def warm_white
|
17
|
+
@state[:w]
|
18
|
+
end
|
19
|
+
|
20
|
+
def cold_white
|
21
|
+
@state[:c]
|
22
|
+
end
|
23
|
+
|
24
|
+
def rgb
|
25
|
+
{ red: @state[:r], green: @state[:g], blue: @state[:b] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def color_temp
|
29
|
+
@state[:temp]
|
30
|
+
end
|
31
|
+
|
32
|
+
def brightness
|
33
|
+
@state[:dimming]
|
34
|
+
end
|
35
|
+
|
36
|
+
def speed
|
37
|
+
@state[:speed]
|
38
|
+
end
|
39
|
+
|
40
|
+
def scene
|
41
|
+
Wizrb::Lighting::SCENES.key(@state[:schdPsetId] || @state[:sceneId])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wizrb
|
4
|
+
module Lighting
|
5
|
+
SCENES = {
|
6
|
+
ocean: 1,
|
7
|
+
romance: 2,
|
8
|
+
sunset: 3,
|
9
|
+
party: 4,
|
10
|
+
fireplace: 5,
|
11
|
+
cozy: 6,
|
12
|
+
forest: 7,
|
13
|
+
pastel_colors: 8,
|
14
|
+
wake_up: 9,
|
15
|
+
bedtime: 10,
|
16
|
+
warm_white: 11,
|
17
|
+
daylight: 12,
|
18
|
+
cool_white: 13,
|
19
|
+
night_light: 14,
|
20
|
+
focus: 15,
|
21
|
+
relax: 16,
|
22
|
+
true_colors: 17,
|
23
|
+
tv_time: 18,
|
24
|
+
plantgrowth: 19,
|
25
|
+
spring: 20,
|
26
|
+
summer: 21,
|
27
|
+
fall: 22,
|
28
|
+
deepdive: 23,
|
29
|
+
jungle: 24,
|
30
|
+
mojito: 25,
|
31
|
+
club: 26,
|
32
|
+
christmas: 27,
|
33
|
+
halloween: 28,
|
34
|
+
candlelight: 29,
|
35
|
+
golden_white: 30,
|
36
|
+
pulse: 31,
|
37
|
+
steampunk: 32,
|
38
|
+
rhythm: 1000
|
39
|
+
}.freeze
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../shared/discover'
|
4
|
+
require_relative 'group'
|
5
|
+
require_relative 'products/smart_plug'
|
6
|
+
|
7
|
+
module Wizrb
|
8
|
+
module Power
|
9
|
+
class Discover < Wizrb::Shared::Discover
|
10
|
+
private
|
11
|
+
|
12
|
+
def parse_response(data, addr)
|
13
|
+
response = JSON.parse(data)
|
14
|
+
return unless response.dig('result', 'success') && addr[1] && addr[2]
|
15
|
+
|
16
|
+
resolve_device(ip: addr[2], port: addr[1])
|
17
|
+
rescue StandardError
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def resolve_device(ip:, port: 38_899)
|
22
|
+
module_name = Wizrb::Shared::Products::Device.new(ip: ip, port: port).module_name
|
23
|
+
|
24
|
+
return unless module_name.include?(Wizrb::Power::Products::SmartPlug::MODULE_NAME_IDENTIFIER)
|
25
|
+
|
26
|
+
Wizrb::Power::Products::SmartPlug.new(ip: ip, port: port)
|
27
|
+
end
|
28
|
+
|
29
|
+
def group_devices
|
30
|
+
Wizrb::Power::Group.new(devices: @devices)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../shared/products/device'
|
4
|
+
|
5
|
+
module Wizrb
|
6
|
+
module Power
|
7
|
+
module Products
|
8
|
+
class SmartPlug < Wizrb::Shared::Products::Device
|
9
|
+
MODULE_NAME_IDENTIFIER = 'SOCKET'
|
10
|
+
|
11
|
+
def initialize(ip:, port: 38_899)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
module Wizrb
|
7
|
+
module Shared
|
8
|
+
class Connection
|
9
|
+
attr_reader :ip, :port, :connection_id, :socket
|
10
|
+
|
11
|
+
def initialize(ip, port)
|
12
|
+
@ip = ip
|
13
|
+
@port = port
|
14
|
+
@connection_id = Time.now.to_i.to_s(16)
|
15
|
+
@socket = UDPSocket.new
|
16
|
+
log('Created')
|
17
|
+
end
|
18
|
+
|
19
|
+
def connect
|
20
|
+
with_error_logging do
|
21
|
+
socket.connect(ip, port)
|
22
|
+
log('Connected')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def send(data)
|
27
|
+
with_error_logging do
|
28
|
+
connect
|
29
|
+
log("Sending: #{data.to_json}")
|
30
|
+
socket.send(data.to_json.encode('UTF-8'), 0)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def receive(timeout: 2, max: 1024)
|
35
|
+
with_error_logging do
|
36
|
+
connect
|
37
|
+
|
38
|
+
ready = socket.wait_readable(timeout)
|
39
|
+
raise Wizrb::ConnectionTimeoutError unless ready
|
40
|
+
|
41
|
+
data, _addr = socket.recvfrom(max)
|
42
|
+
log("Received: #{data}")
|
43
|
+
parse_response(data)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test
|
48
|
+
with_error_logging do
|
49
|
+
send({ method: 'getPilot', params: {} })
|
50
|
+
receive
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def parse_response(data)
|
57
|
+
response = JSON.parse(data)
|
58
|
+
|
59
|
+
raise Wizrb::ConnectionError, response['error'] if response.key?('error')
|
60
|
+
|
61
|
+
response
|
62
|
+
end
|
63
|
+
|
64
|
+
def log(message)
|
65
|
+
puts "[Wizrb::Connection##{connection_id} #{ip}:#{port}] #{message}" if ENV['DEBUG']
|
66
|
+
end
|
67
|
+
|
68
|
+
def with_error_logging
|
69
|
+
yield
|
70
|
+
rescue StandardError => e
|
71
|
+
log("Error: #{e.message}")
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|