chromecast 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.rdoc +47 -0
- data/chromecast.gemspec +31 -0
- data/init.rb +1 -0
- data/lib/chromecast.rb +13 -0
- data/lib/chromecast/application.rb +7 -0
- data/lib/chromecast/channel.rb +10 -0
- data/lib/chromecast/channel/base.rb +52 -0
- data/lib/chromecast/channel/connection.rb +19 -0
- data/lib/chromecast/channel/heartbeat.rb +20 -0
- data/lib/chromecast/channel/media.rb +34 -0
- data/lib/chromecast/channel/receiver.rb +51 -0
- data/lib/chromecast/connection.rb +87 -0
- data/lib/chromecast/protocol.pb.rb +68 -0
- data/lib/chromecast/tasks.rb +24 -0
- data/protocol.proto +79 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ab0190901c9fd66715415de2eded3dea8404e4cb
|
4
|
+
data.tar.gz: 3ce530143c686dc4cb515e708ab6b00999af77fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6f3a8461d4a2260def97f0f7c7f1c06fe66f4d994e0ec733899c399d1d5d2d0ac9bc53aae4c727a243fa64e5f03c2b190a1b2148e726e04787bf73d516fb9c9
|
7
|
+
data.tar.gz: 0b80dd58061653159e6646792aca290fca6f2fa80a49c19f79f5d08c0c3b8da888970ba21e06860f3eaad07a2f7d102cfcbcab2360b34de2fa00e96353493a24
|
data/README.rdoc
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
= chromecast
|
2
|
+
|
3
|
+
A simple Ruby library you can use to monitor and control basic features of
|
4
|
+
your Chromecast.
|
5
|
+
|
6
|
+
== Quick start
|
7
|
+
|
8
|
+
Install the gem:
|
9
|
+
|
10
|
+
gem install chromecast
|
11
|
+
|
12
|
+
Or add it to your Gemfile:
|
13
|
+
|
14
|
+
gem "chromecast"
|
15
|
+
|
16
|
+
Generate a certificate to be used for TLS connetion:
|
17
|
+
|
18
|
+
bundle exec rake setup:certificate
|
19
|
+
|
20
|
+
Use it:
|
21
|
+
|
22
|
+
require 'chromecast'
|
23
|
+
|
24
|
+
c = Chromecast::Connection.new(CHROMECAST_IP).open
|
25
|
+
c.connection.connect
|
26
|
+
|
27
|
+
while true
|
28
|
+
msg = c.read
|
29
|
+
|
30
|
+
puts msg
|
31
|
+
|
32
|
+
if msg['type'] == 'PING'
|
33
|
+
c.heartbeat.pong
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
== To Do
|
38
|
+
|
39
|
+
* Scanning for devices
|
40
|
+
* EventMachine support
|
41
|
+
* Proper documentation
|
42
|
+
* Tests
|
43
|
+
* Support for more namespaces
|
44
|
+
|
45
|
+
== License
|
46
|
+
|
47
|
+
This gem is released under the {MIT License}[http://www.opensource.org/licenses/MIT].
|
data/chromecast.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "chromecast"
|
3
|
+
s.version = "1.0"
|
4
|
+
s.date = "2014-10-04"
|
5
|
+
s.summary = "Basic Chromecast controll via a Ruby library"
|
6
|
+
s.description = "The gem lets you monitor Chromecast status, control volume and launch apps."
|
7
|
+
s.authors = ["Robert Nasiadek"]
|
8
|
+
s.files = [
|
9
|
+
"init.rb",
|
10
|
+
"lib/chromecast.rb",
|
11
|
+
"lib/chromecast/tasks.rb",
|
12
|
+
"lib/chromecast/protocol.pb.rb",
|
13
|
+
"lib/chromecast/channel/media.rb",
|
14
|
+
"lib/chromecast/channel/connection.rb",
|
15
|
+
"lib/chromecast/channel/base.rb",
|
16
|
+
"lib/chromecast/channel/receiver.rb",
|
17
|
+
"lib/chromecast/channel/heartbeat.rb",
|
18
|
+
"lib/chromecast/connection.rb",
|
19
|
+
"lib/chromecast/application.rb",
|
20
|
+
"lib/chromecast/channel.rb",
|
21
|
+
"protocol.proto",
|
22
|
+
"README.rdoc",
|
23
|
+
"chromecast.gemspec"
|
24
|
+
]
|
25
|
+
s.email = "robert@kapati.net"
|
26
|
+
s.homepage = "http://robzon.pl/"
|
27
|
+
s.license = "MIT"
|
28
|
+
|
29
|
+
s.add_runtime_dependency 'ruby-tls', '~> 1.0'
|
30
|
+
s.add_runtime_dependency 'protobuf', '~> 3.3'
|
31
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'chromecast'
|
data/lib/chromecast.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'protobuf'
|
3
|
+
|
4
|
+
require 'chromecast/protocol.pb'
|
5
|
+
require 'chromecast/connection'
|
6
|
+
require 'chromecast/channel'
|
7
|
+
require 'chromecast/application'
|
8
|
+
|
9
|
+
module Chromecast
|
10
|
+
APP_DEFAULT_MEDIA_RECEIVER = 'CC1AD845'
|
11
|
+
APP_BACKDROP = 'E8C28D3C'
|
12
|
+
APP_YOUTUBE = '233637DE'
|
13
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Chromecast
|
2
|
+
module Channel
|
3
|
+
class Base
|
4
|
+
attr_accessor :connection, :namespace, :type
|
5
|
+
|
6
|
+
def initialize connection, namespace, type
|
7
|
+
self.connection = connection
|
8
|
+
self.namespace = namespace
|
9
|
+
self.type = type
|
10
|
+
end
|
11
|
+
|
12
|
+
def send data
|
13
|
+
#puts "SEND: #{data}"
|
14
|
+
#puts ""
|
15
|
+
|
16
|
+
msg = new_message(data)
|
17
|
+
|
18
|
+
encoded = msg.encode
|
19
|
+
size = [encoded.size].pack('N')
|
20
|
+
|
21
|
+
connection.write(size + encoded)
|
22
|
+
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def new_message(payload)
|
29
|
+
m = Protocol::CastMessage.new
|
30
|
+
|
31
|
+
m.protocol_version = Protocol::CastMessage::ProtocolVersion::CASTV2_1_0
|
32
|
+
m.source_id = connection.source
|
33
|
+
m.destination_id = connection.destination
|
34
|
+
m.namespace = namespace
|
35
|
+
|
36
|
+
case type
|
37
|
+
when :json
|
38
|
+
m.payload_type = Protocol::CastMessage::PayloadType::STRING
|
39
|
+
m.payload_utf8 = payload.to_json
|
40
|
+
when :string
|
41
|
+
m.payload_type = Protocol::CastMessage::PayloadType::STRING
|
42
|
+
m.payload_utf8 = payload
|
43
|
+
when :binary
|
44
|
+
m.payload_type = Protocol::CastMessage::PayloadType::BINARY
|
45
|
+
m.payload_binary = payload
|
46
|
+
end
|
47
|
+
|
48
|
+
return m
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Chromecast
|
2
|
+
module Channel
|
3
|
+
class Connection < Base
|
4
|
+
NAMESPACE = 'urn:x-cast:com.google.cast.tp.connection'
|
5
|
+
|
6
|
+
def initialize connection
|
7
|
+
super(connection, NAMESPACE, :json)
|
8
|
+
end
|
9
|
+
|
10
|
+
def connect
|
11
|
+
send(type: 'CONNECT')
|
12
|
+
end
|
13
|
+
|
14
|
+
def close
|
15
|
+
send(type: 'CLOSE')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Chromecast
|
2
|
+
module Channel
|
3
|
+
class Heartbeat < Base
|
4
|
+
NAMESPACE = 'urn:x-cast:com.google.cast.tp.heartbeat'
|
5
|
+
|
6
|
+
def initialize connection
|
7
|
+
super(connection, NAMESPACE, :json)
|
8
|
+
end
|
9
|
+
|
10
|
+
def ping
|
11
|
+
send(type: 'PING')
|
12
|
+
end
|
13
|
+
|
14
|
+
def pong
|
15
|
+
send(type: 'PONG')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Chromecast
|
2
|
+
module Channel
|
3
|
+
class Media < Base
|
4
|
+
NAMESPACE = 'urn:x-cast:com.google.cast.media'
|
5
|
+
|
6
|
+
def initialize connection
|
7
|
+
super(connection, NAMESPACE, :json)
|
8
|
+
@request_id = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_status
|
12
|
+
send(type: 'GET_STATUS')
|
13
|
+
end
|
14
|
+
|
15
|
+
def pause
|
16
|
+
send(type: 'PAUSE')
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def next_request_id
|
22
|
+
@request_id += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def send data
|
26
|
+
super(data.merge(
|
27
|
+
requestId: next_request_id,
|
28
|
+
mediaSessionId: '0B5C6066-F8B2-77F4-E5F1-8CEC223F0A22'
|
29
|
+
))
|
30
|
+
@request_id
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Chromecast
|
2
|
+
module Channel
|
3
|
+
class Receiver < Base
|
4
|
+
NAMESPACE = 'urn:x-cast:com.google.cast.receiver'
|
5
|
+
|
6
|
+
def initialize connection
|
7
|
+
super(connection, NAMESPACE, :json)
|
8
|
+
@request_id = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def launch app_id
|
12
|
+
send(type: 'LAUNCH', appId: app_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop session_id
|
16
|
+
send(type: 'STOP', sessionId: session_id)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_status
|
20
|
+
send(type: 'GET_STATUS')
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_app_availability apps
|
24
|
+
send(type: 'GET_APP_AVAILABILITY', appId: [apps].flatten)
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_volume level
|
28
|
+
send(type: 'SET_VOLUME', volume: {level: level})
|
29
|
+
end
|
30
|
+
|
31
|
+
def mute
|
32
|
+
send(type: 'SET_VOLUME', volume: {muted: true})
|
33
|
+
end
|
34
|
+
|
35
|
+
def unmute
|
36
|
+
send(type: 'SET_VOLUME', volume: {muted: false})
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def next_request_id
|
42
|
+
@request_id += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def send data
|
46
|
+
super(data.merge(requestId: next_request_id))
|
47
|
+
@request_id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require 'socket'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
module Chromecast
|
7
|
+
class Connection
|
8
|
+
DEFAULT_PORT = 8009
|
9
|
+
DEFAULT_SOURCE = 'source-0'
|
10
|
+
DEFAULT_DESTINATION = 'receiver-0'
|
11
|
+
|
12
|
+
attr_reader :host, :port, :source, :destination, :socket
|
13
|
+
attr_accessor :key_file, :crt_file
|
14
|
+
|
15
|
+
def initialize(host)
|
16
|
+
@host = host
|
17
|
+
@port = DEFAULT_PORT
|
18
|
+
@source = DEFAULT_SOURCE
|
19
|
+
@destination = DEFAULT_DESTINATION
|
20
|
+
@key_file = 'certificate.key'
|
21
|
+
@crt_file = 'certificate.crt'
|
22
|
+
end
|
23
|
+
|
24
|
+
def certificate(type)
|
25
|
+
case type
|
26
|
+
when :crt
|
27
|
+
return File.open(crt_file).read
|
28
|
+
when :key
|
29
|
+
return File.open(key_file).read
|
30
|
+
end
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def open
|
35
|
+
tcp = TCPSocket.new(host, port)
|
36
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
37
|
+
ctx.cert = OpenSSL::X509::Certificate.new(certificate(:crt))
|
38
|
+
ctx.key = OpenSSL::PKey::RSA.new(certificate(:key))
|
39
|
+
ctx.ssl_version = :SSLv23
|
40
|
+
ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
41
|
+
|
42
|
+
@socket = OpenSSL::SSL::SSLSocket.new(tcp, ctx).tap do |socket|
|
43
|
+
socket.sync_close = true
|
44
|
+
socket.connect
|
45
|
+
end
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def write data
|
51
|
+
@socket.write data
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_data?
|
55
|
+
ready = IO.select([@socket], nil, nil, 0)
|
56
|
+
|
57
|
+
return ready != nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def read
|
61
|
+
return nil unless has_data?
|
62
|
+
|
63
|
+
resp_size = @socket.read(4).unpack('N').first
|
64
|
+
resp_data = @socket.read(resp_size)
|
65
|
+
|
66
|
+
resp = Protocol::CastMessage.decode(resp_data)
|
67
|
+
|
68
|
+
JSON.parse(resp.payload_utf8)
|
69
|
+
end
|
70
|
+
|
71
|
+
def connection
|
72
|
+
@connection_channel ||= Channel::Connection.new(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
def receiver
|
76
|
+
@receiver_channel ||= Channel::Receiver.new(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
def heartbeat
|
80
|
+
@heartbeat_channel ||= Channel::Heartbeat.new(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
def media
|
84
|
+
@media_channel ||= Channel::Media.new(self)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
##
|
2
|
+
# This file is auto-generated. DO NOT EDIT!
|
3
|
+
#
|
4
|
+
require 'protobuf/message'
|
5
|
+
|
6
|
+
module Chromecast
|
7
|
+
module Protocol
|
8
|
+
|
9
|
+
##
|
10
|
+
# Message Classes
|
11
|
+
#
|
12
|
+
class CastMessage < ::Protobuf::Message
|
13
|
+
class ProtocolVersion < ::Protobuf::Enum
|
14
|
+
define :CASTV2_1_0, 0
|
15
|
+
end
|
16
|
+
|
17
|
+
class PayloadType < ::Protobuf::Enum
|
18
|
+
define :STRING, 0
|
19
|
+
define :BINARY, 1
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class AuthChallenge < ::Protobuf::Message; end
|
25
|
+
class AuthResponse < ::Protobuf::Message; end
|
26
|
+
class AuthError < ::Protobuf::Message
|
27
|
+
class ErrorType < ::Protobuf::Enum
|
28
|
+
define :INTERNAL_ERROR, 0
|
29
|
+
define :NO_TLS, 1
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class DeviceAuthMessage < ::Protobuf::Message; end
|
35
|
+
|
36
|
+
|
37
|
+
##
|
38
|
+
# Message Fields
|
39
|
+
#
|
40
|
+
class CastMessage
|
41
|
+
required ::Chromecast::Protocol::CastMessage::ProtocolVersion, :protocol_version, 1
|
42
|
+
required :string, :source_id, 2
|
43
|
+
required :string, :destination_id, 3
|
44
|
+
required :string, :namespace, 4
|
45
|
+
required ::Chromecast::Protocol::CastMessage::PayloadType, :payload_type, 5
|
46
|
+
optional :string, :payload_utf8, 6
|
47
|
+
optional :bytes, :payload_binary, 7
|
48
|
+
end
|
49
|
+
|
50
|
+
class AuthResponse
|
51
|
+
required :bytes, :signature, 1
|
52
|
+
required :bytes, :client_auth_certificate, 2
|
53
|
+
end
|
54
|
+
|
55
|
+
class AuthError
|
56
|
+
required ::Chromecast::Protocol::AuthError::ErrorType, :error_type, 1
|
57
|
+
end
|
58
|
+
|
59
|
+
class DeviceAuthMessage
|
60
|
+
optional ::Chromecast::Protocol::AuthChallenge, :challenge, 1
|
61
|
+
optional ::Chromecast::Protocol::AuthResponse, :response, 2
|
62
|
+
optional ::Chromecast::Protocol::AuthError, :error, 3
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Chromecast
|
2
|
+
class Tasks
|
3
|
+
include Rake::DSL if defined? Rake::DSL
|
4
|
+
|
5
|
+
def install_tasks
|
6
|
+
desc "Setup all necessary files"
|
7
|
+
task setup: ['setup:protocol', 'setup:certificate']
|
8
|
+
|
9
|
+
namespace :setup do
|
10
|
+
desc "Generate certificates for TLS"
|
11
|
+
task :certificate do
|
12
|
+
system('openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certificate.key -out certificate.crt')
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Generate Ruby code for Chromecast protocol"
|
16
|
+
task :protocol do
|
17
|
+
system('protoc --ruby_out lib/chromecast/ protocol.proto && echo Generated protocol code')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Chromecast::Tasks.new.install_tasks
|
data/protocol.proto
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
// Copyright 2013 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
syntax = "proto2";
|
6
|
+
|
7
|
+
option optimize_for = LITE_RUNTIME;
|
8
|
+
|
9
|
+
package Chromecast.Protocol;
|
10
|
+
|
11
|
+
message CastMessage {
|
12
|
+
// Always pass a version of the protocol for future compatibility
|
13
|
+
// requirements.
|
14
|
+
enum ProtocolVersion {
|
15
|
+
CASTV2_1_0 = 0;
|
16
|
+
}
|
17
|
+
required ProtocolVersion protocol_version = 1;
|
18
|
+
|
19
|
+
// source and destination ids identify the origin and destination of the
|
20
|
+
// message. They are used to route messages between endpoints that share a
|
21
|
+
// device-to-device channel.
|
22
|
+
//
|
23
|
+
// For messages between applications:
|
24
|
+
// - The sender application id is a unique identifier generated on behalf of
|
25
|
+
// the sender application.
|
26
|
+
// - The receiver id is always the the session id for the application.
|
27
|
+
//
|
28
|
+
// For messages to or from the sender or receiver platform, the special ids
|
29
|
+
// 'sender-0' and 'receiver-0' can be used.
|
30
|
+
//
|
31
|
+
// For messages intended for all endpoints using a given channel, the
|
32
|
+
// wildcard destination_id '*' can be used.
|
33
|
+
required string source_id = 2;
|
34
|
+
required string destination_id = 3;
|
35
|
+
|
36
|
+
// This is the core multiplexing key. All messages are sent on a namespace
|
37
|
+
// and endpoints sharing a channel listen on one or more namespaces. The
|
38
|
+
// namespace defines the protocol and semantics of the message.
|
39
|
+
required string namespace = 4;
|
40
|
+
|
41
|
+
// Encoding and payload info follows.
|
42
|
+
|
43
|
+
// What type of data do we have in this message.
|
44
|
+
enum PayloadType {
|
45
|
+
STRING = 0;
|
46
|
+
BINARY = 1;
|
47
|
+
}
|
48
|
+
required PayloadType payload_type = 5;
|
49
|
+
|
50
|
+
// Depending on payload_type, exactly one of the following optional fields
|
51
|
+
// will always be set.
|
52
|
+
optional string payload_utf8 = 6;
|
53
|
+
optional bytes payload_binary = 7;
|
54
|
+
}
|
55
|
+
|
56
|
+
// Messages for authentication protocol between a sender and a receiver.
|
57
|
+
message AuthChallenge {
|
58
|
+
}
|
59
|
+
|
60
|
+
message AuthResponse {
|
61
|
+
required bytes signature = 1;
|
62
|
+
required bytes client_auth_certificate = 2;
|
63
|
+
}
|
64
|
+
|
65
|
+
message AuthError {
|
66
|
+
enum ErrorType {
|
67
|
+
INTERNAL_ERROR = 0;
|
68
|
+
NO_TLS = 1; // The underlying connection is not TLS
|
69
|
+
}
|
70
|
+
required ErrorType error_type = 1;
|
71
|
+
}
|
72
|
+
|
73
|
+
message DeviceAuthMessage {
|
74
|
+
// Request fields
|
75
|
+
optional AuthChallenge challenge = 1;
|
76
|
+
// Response fields
|
77
|
+
optional AuthResponse response = 2;
|
78
|
+
optional AuthError error = 3;
|
79
|
+
}
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chromecast
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Nasiadek
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-tls
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: protobuf
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.3'
|
41
|
+
description: The gem lets you monitor Chromecast status, control volume and launch
|
42
|
+
apps.
|
43
|
+
email: robert@kapati.net
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.rdoc
|
49
|
+
- chromecast.gemspec
|
50
|
+
- init.rb
|
51
|
+
- lib/chromecast.rb
|
52
|
+
- lib/chromecast/application.rb
|
53
|
+
- lib/chromecast/channel.rb
|
54
|
+
- lib/chromecast/channel/base.rb
|
55
|
+
- lib/chromecast/channel/connection.rb
|
56
|
+
- lib/chromecast/channel/heartbeat.rb
|
57
|
+
- lib/chromecast/channel/media.rb
|
58
|
+
- lib/chromecast/channel/receiver.rb
|
59
|
+
- lib/chromecast/connection.rb
|
60
|
+
- lib/chromecast/protocol.pb.rb
|
61
|
+
- lib/chromecast/tasks.rb
|
62
|
+
- protocol.proto
|
63
|
+
homepage: http://robzon.pl/
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata: {}
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 2.2.1
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Basic Chromecast controll via a Ruby library
|
87
|
+
test_files: []
|