chromecast 1.0

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: ab0190901c9fd66715415de2eded3dea8404e4cb
4
+ data.tar.gz: 3ce530143c686dc4cb515e708ab6b00999af77fd
5
+ SHA512:
6
+ metadata.gz: a6f3a8461d4a2260def97f0f7c7f1c06fe66f4d994e0ec733899c399d1d5d2d0ac9bc53aae4c727a243fa64e5f03c2b190a1b2148e726e04787bf73d516fb9c9
7
+ data.tar.gz: 0b80dd58061653159e6646792aca290fca6f2fa80a49c19f79f5d08c0c3b8da888970ba21e06860f3eaad07a2f7d102cfcbcab2360b34de2fa00e96353493a24
@@ -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].
@@ -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'
@@ -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,7 @@
1
+ module Chromecast
2
+ module Application
3
+ DEFAULT_MEDIA_RECEIVER = 'CC1AD845'
4
+ BACKDROP = 'E8C28D3C'
5
+ YOUTUBE = '233637DE'
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'channel/base'
2
+ require_relative 'channel/connection'
3
+ require_relative 'channel/receiver'
4
+ require_relative 'channel/heartbeat'
5
+ require_relative 'channel/media'
6
+
7
+ module Chromecast
8
+ module Channel
9
+ end
10
+ 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
@@ -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: []