lightpack 0.0.1
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.
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +124 -0
- data/Rakefile +17 -0
- data/lib/lightpack.rb +245 -0
- data/lib/lightpack/version.rb +3 -0
- metadata +79 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Douwe Maan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# lightpack
|
2
|
+
|
3
|
+
A Ruby library for communicating with your [Lightpack](http://lightpack.tv).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```sh
|
8
|
+
gem install lightpack
|
9
|
+
```
|
10
|
+
|
11
|
+
Or in your Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "lightpack"
|
15
|
+
```
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require "lightpack"
|
21
|
+
|
22
|
+
# You can open a connection to the Lightpack that will live for the duration of the block using `.open`:
|
23
|
+
Lightpack.open do |lightpack|
|
24
|
+
# Use `lightpack` as described below.
|
25
|
+
end
|
26
|
+
|
27
|
+
# If you want a little more control over opening and closing the connection, just use `.new`:
|
28
|
+
lightpack = Lightpack.new("127.0.0.1", 3636, "api_key")
|
29
|
+
# "127.0.0.1" and 3636 are the default host and port and can be omitted as in the `.open` example.
|
30
|
+
# The third parameter is the optional api key, which you need to set
|
31
|
+
# if you've set up your Lightpack to require authentication.
|
32
|
+
|
33
|
+
# You can then explicitly open a connection to the lightpack.
|
34
|
+
# You're responsible for closing it using `#disconnect` when you're done.
|
35
|
+
lightpack.connect
|
36
|
+
|
37
|
+
# If you need to authenticate but haven't done so yet with `.open` or `.new`,
|
38
|
+
# you can do so manually after connecting.
|
39
|
+
lightpack.authenticate("api_key")
|
40
|
+
|
41
|
+
# The Lightpack object exposes a lot of interesting information:
|
42
|
+
lightpack.status # :on, :off, :device_error or :unknown
|
43
|
+
lightpack.on? # true or false
|
44
|
+
lightpack.api_status # :idle or :busy
|
45
|
+
|
46
|
+
lightpack.profiles # ["Lightpack", "Other profile"]
|
47
|
+
lightpack.profile # "Lightpack"
|
48
|
+
|
49
|
+
lightpack.led_count # 10
|
50
|
+
lightpack.led_areas # [{:x=>1920, :y=>1224, :width=>640, :height=>216}, {:x=>2176, :y=>720, ...}, ...]
|
51
|
+
lightpack.colors # [[170, 170, 173], [190, 190, 192], ...] # Red, Green, Blue
|
52
|
+
|
53
|
+
lightpack.fps # 20.04
|
54
|
+
lightpack.screen_size # [0, 0, 2560, 1440]
|
55
|
+
lightpack.mode # :ambilight or :moodlamp
|
56
|
+
|
57
|
+
# Of course, you can also _do_ certain things with your Lightpack.
|
58
|
+
# All of these return true when successful and raise an error otherwise.
|
59
|
+
lightpack.turn_on
|
60
|
+
lightpack.turn_off
|
61
|
+
|
62
|
+
lightpack.profile = "Other profile"
|
63
|
+
lightpack.mode = :moodlamp
|
64
|
+
|
65
|
+
lightpack.gamma = 2.5 # 0.1..10
|
66
|
+
lightpack.brightness = 93 # 0..100
|
67
|
+
lightpack.smooth = 128 # 0..255
|
68
|
+
|
69
|
+
lightpack.set_color(0, 255, 0, 0) # index of LED, Red, Green, Blue
|
70
|
+
lightpack.set_all_colors(0, 255, 0)
|
71
|
+
lightpack.set_led_areas(0, { x: 1920, y: 1224, width: 640, height: 216 })
|
72
|
+
lightpack.add_profile("New profile")
|
73
|
+
lightpack.delete_profile("Other profile")
|
74
|
+
|
75
|
+
# Note that all of the action methods above require a lock on the Lightpack.
|
76
|
+
# This lock is acquired automatically if you haven't done so manually,
|
77
|
+
# but if you want to do a number of actions sequentially, acquiring a lock once
|
78
|
+
# would be more efficient than acquiring and releasing one for every action.
|
79
|
+
|
80
|
+
# You can acquire a lock for the duration of a block like this:
|
81
|
+
lightpack.with_lock do
|
82
|
+
lightpack.set_color(0, 255, 0, 0)
|
83
|
+
lightpack.set_color(1, 0, 255, 0)
|
84
|
+
lightpack.set_color(2, 0, 0, 255)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Alternatively, you can use `#lock` in combination with `#unlock`.
|
88
|
+
lightpack.lock
|
89
|
+
lightpack.gamma = 2.5
|
90
|
+
lightpack.brightness = 93
|
91
|
+
lightpack.smooth = 128
|
92
|
+
lightpack.unlock
|
93
|
+
|
94
|
+
# If you want to, you can check whether a lock is active:
|
95
|
+
lightpack.locked?
|
96
|
+
|
97
|
+
# When you're done, you can just let the program exit or disconnect explicitly.
|
98
|
+
lightpack.disconnect
|
99
|
+
```
|
100
|
+
|
101
|
+
## Examples
|
102
|
+
Check out the [`examples/`](examples) folder for some basic examples.
|
103
|
+
|
104
|
+
## License
|
105
|
+
Copyright (c) 2014 Douwe Maan
|
106
|
+
|
107
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
108
|
+
a copy of this software and associated documentation files (the
|
109
|
+
"Software"), to deal in the Software without restriction, including
|
110
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
111
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
112
|
+
permit persons to whom the Software is furnished to do so, subject to
|
113
|
+
the following conditions:
|
114
|
+
|
115
|
+
The above copyright notice and this permission notice shall be
|
116
|
+
included in all copies or substantial portions of the Software.
|
117
|
+
|
118
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
119
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
120
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
121
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
122
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
123
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
124
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rspec/core/rake_task"
|
2
|
+
|
3
|
+
spec = Gem::Specification.load("lightpack.gemspec")
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task default: :spec
|
8
|
+
|
9
|
+
desc "Build the .gem file"
|
10
|
+
task :build do
|
11
|
+
system "gem build #{spec.name}.gemspec"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Push the .gem file to rubygems.org"
|
15
|
+
task release: :build do
|
16
|
+
system "gem push #{spec.name}-#{spec.version}.gem"
|
17
|
+
end
|
data/lib/lightpack.rb
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
class Lightpack
|
4
|
+
module Errors
|
5
|
+
class NotConnected < StandardError; end
|
6
|
+
class AuthenticationFailed < StandardError; end
|
7
|
+
|
8
|
+
class AuthenticationRequired < StandardError; end
|
9
|
+
class UnknownCommand < StandardError; end
|
10
|
+
class NotLocked < StandardError; end
|
11
|
+
class Busy < StandardError; end
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
MESSAGES = {
|
15
|
+
"authorization required" => AuthenticationRequired,
|
16
|
+
"unknown command" => UnknownCommand,
|
17
|
+
"not locked" => NotLocked,
|
18
|
+
"busy" => Busy,
|
19
|
+
"error" => Error
|
20
|
+
}.freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.open(*args)
|
24
|
+
pack = new(*args)
|
25
|
+
|
26
|
+
begin
|
27
|
+
pack.connect
|
28
|
+
yield pack
|
29
|
+
ensure
|
30
|
+
pack.disconnect
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :host, :port
|
35
|
+
attr_accessor :api_key
|
36
|
+
|
37
|
+
def initialize(host = "127.0.0.1", port = 3636, api_key = nil)
|
38
|
+
@host = host
|
39
|
+
@port = port
|
40
|
+
@api_key = api_key
|
41
|
+
end
|
42
|
+
|
43
|
+
def connected?
|
44
|
+
@connected
|
45
|
+
end
|
46
|
+
|
47
|
+
def connect
|
48
|
+
return false if connected?
|
49
|
+
|
50
|
+
@socket = TCPSocket.new(@host, @port)
|
51
|
+
@connected = true
|
52
|
+
|
53
|
+
@socket.gets # Read welcome message
|
54
|
+
|
55
|
+
unless authenticate
|
56
|
+
disconnect
|
57
|
+
raise Errors::AuthenticationFailed
|
58
|
+
end
|
59
|
+
|
60
|
+
true
|
61
|
+
rescue
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def disconnect
|
66
|
+
return false unless connected?
|
67
|
+
|
68
|
+
unlock
|
69
|
+
|
70
|
+
@connected = false
|
71
|
+
|
72
|
+
@socket.close if @socket && !@socket.closed?
|
73
|
+
@socket = nil
|
74
|
+
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def authenticate(api_key = @api_key)
|
79
|
+
return true unless api_key
|
80
|
+
|
81
|
+
command("apikey:#{api_key}") == true
|
82
|
+
end
|
83
|
+
|
84
|
+
def locked?
|
85
|
+
@locked
|
86
|
+
end
|
87
|
+
|
88
|
+
def lock
|
89
|
+
return true if locked?
|
90
|
+
|
91
|
+
result = command("lock")
|
92
|
+
@locked = result == :success
|
93
|
+
end
|
94
|
+
|
95
|
+
def unlock
|
96
|
+
return false unless locked?
|
97
|
+
|
98
|
+
result = command("unlock")
|
99
|
+
@locked = false
|
100
|
+
|
101
|
+
result == :success
|
102
|
+
end
|
103
|
+
|
104
|
+
def with_lock(&block)
|
105
|
+
already_locked = locked?
|
106
|
+
|
107
|
+
lock unless already_locked
|
108
|
+
|
109
|
+
yield self
|
110
|
+
ensure
|
111
|
+
unlock unless already_locked
|
112
|
+
end
|
113
|
+
|
114
|
+
# Getters
|
115
|
+
def status
|
116
|
+
command("getstatus").gsub(" ", "_").to_sym
|
117
|
+
end
|
118
|
+
|
119
|
+
def on?
|
120
|
+
status == :on
|
121
|
+
end
|
122
|
+
|
123
|
+
def api_status
|
124
|
+
command("getstatusapi").gsub(" ", "_").to_sym
|
125
|
+
end
|
126
|
+
|
127
|
+
def profiles
|
128
|
+
command("getprofiles").split(";")
|
129
|
+
end
|
130
|
+
|
131
|
+
def profile
|
132
|
+
command("getprofile")
|
133
|
+
end
|
134
|
+
|
135
|
+
def led_count
|
136
|
+
command("getcountleds").to_i
|
137
|
+
end
|
138
|
+
|
139
|
+
def led_areas
|
140
|
+
command("getleds").split(";").map do |info|
|
141
|
+
keys = [:x, :y, :width, :height]
|
142
|
+
dimensions = info.split("-", 2)[1].split(",").map(&:to_i)
|
143
|
+
|
144
|
+
Hash[keys.zip(dimensions)]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def colors
|
149
|
+
command("getcolors").split(";").map do |info|
|
150
|
+
info.split("-", 2)[1].split(",").map(&:to_i)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def fps
|
155
|
+
command("getfps").to_f
|
156
|
+
end
|
157
|
+
|
158
|
+
def screen_size
|
159
|
+
command("getscreensize").split(",").map(&:to_i)
|
160
|
+
end
|
161
|
+
|
162
|
+
def mode
|
163
|
+
command("getmode").to_sym
|
164
|
+
end
|
165
|
+
|
166
|
+
# Setters
|
167
|
+
def turn_on
|
168
|
+
with_lock do
|
169
|
+
command("setstatus:on")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def turn_off
|
174
|
+
with_lock do
|
175
|
+
command("setstatus:off")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
[:mode, :gamma, :brightness, :smooth, :profile].each do |key|
|
180
|
+
define_method(:"#{key}=") do |value|
|
181
|
+
with_lock do
|
182
|
+
command("set#{key}:#{value}") && value
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def set_color(n, r, g, b)
|
188
|
+
with_lock do
|
189
|
+
command("setcolor:#{n + 1}-#{[r, g, b].join(",")};")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def set_all_colors(r, g, b)
|
194
|
+
with_lock do
|
195
|
+
colors = [r, g, b].join(",")
|
196
|
+
|
197
|
+
cmd = "setcolor:"
|
198
|
+
led_count.times do |i|
|
199
|
+
cmd << "#{i}-#{colors};"
|
200
|
+
end
|
201
|
+
command cmd
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def set_led_areas(n, dimensions)
|
206
|
+
with_lock do
|
207
|
+
dimensions = [:x, :y, :width, :height].map { |key| dimensions[key] }.join(",")
|
208
|
+
command("setleds:#{n + 1}-#{dimensions};")
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def add_profile(name)
|
213
|
+
with_lock do
|
214
|
+
command("newprofile:#{name}")
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def delete_profile(name)
|
219
|
+
with_lock do
|
220
|
+
command("deleteprofile:#{name}")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
def command(action, type = nil)
|
226
|
+
raise Errors::NotConnected unless connected?
|
227
|
+
|
228
|
+
@socket.puts action
|
229
|
+
result = @socket.recv(8192).chomp
|
230
|
+
|
231
|
+
if error = Errors::MESSAGES[result]
|
232
|
+
raise error
|
233
|
+
end
|
234
|
+
|
235
|
+
if result == "ok"
|
236
|
+
true
|
237
|
+
elsif result =~ /:/
|
238
|
+
result.split(":", 2)[1]
|
239
|
+
else
|
240
|
+
result.gsub(" ", "_").to_sym
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
require "lightpack/version"
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lightpack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Douwe Maan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70272477253440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70272477253440
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70272477250700 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70272477250700
|
36
|
+
description: A Ruby library for communicating with your Lightpack.
|
37
|
+
email: douwe@selenight.nl
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- lib/lightpack/version.rb
|
43
|
+
- lib/lightpack.rb
|
44
|
+
- LICENSE
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- Gemfile
|
48
|
+
homepage: https://github.com/DouweM/lightpack
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
hash: 4336892820563749072
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
hash: 4336892820563749072
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.8.6
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Lightpack communication library
|
79
|
+
test_files: []
|