class Pebble::Watch

Attributes

client_capabilities[RW]
event_handlers[R]
id[R]
protocol[R]
session_capabilities[RW]

Public Class Methods

autodetect() click to toggle source
# File lib/pebble/watch.rb, line 9
def self.autodetect
  return nil unless RUBY_PLATFORM =~ /darwin/

  watches = Dir.glob("/dev/tty.Pebble????-SerialPortSe")

  raise Errors::NoWatchesFound if watches.length == 0
  Pebble.logger.debug "Found multiple watches: #{watches}" if watches.length > 1

  port = watches.first
  id = port[15, 4]
  Pebble.logger.debug "Detected watch with ID #{id}"

  return new(id, port)
end
new(id, port) click to toggle source
# File lib/pebble/watch.rb, line 42
def initialize(id, port)
  @id = id

  @protocol       = Protocol.new(port)
  @event_handlers = Hash.new { |hash, key| hash[key] = [] }

  # We're mirroring Android here.
  @session_capabilities = Capabilities::Session::GAMMA_RAY
  @client_capabilities  = Capabilities::Client::TELEPHONY | Capabilities::Client::SMS | Capabilities::Client::ANDROID

  answer_phone_version_message
  receive_event_messages
  log_log_events
end
open(id, port) { |watch| ... } click to toggle source
# File lib/pebble/watch.rb, line 24
def self.open(id, port)
  watch = new(id, port)

  begin
    watch.connect
    yield watch
  ensure
    watch.disconnect
  end
  nil
end

Public Instance Methods

connect() click to toggle source
# File lib/pebble/watch.rb, line 57
def connect
  @protocol.connect
end
disconnect() click to toggle source
# File lib/pebble/watch.rb, line 61
def disconnect
  @protocol.disconnect
end
get_installed_apps(&async_response_handler) click to toggle source
# File lib/pebble/watch.rb, line 136
def get_installed_apps(&async_response_handler)
  @protocol.send_message(Endpoints::APP_INSTALL_MANAGER, "\x01", async_response_handler) do |message|
    response = {}

    response[:banks_count], apps_count = message[1, 8].unpack("L>L>")
    response[:apps] = []

    size = 78
    apps_count.times do |index|
      offset = index * size + 9

      app = {}

      app[:id], app[:index], app[:name], app[:author], app[:flags], app[:version] = 
        message[offset, size].unpack("L>L>A32A32L>S>")

      response[:apps] << app
    end

    response
  end
end
get_time(&async_response_handler) click to toggle source
# File lib/pebble/watch.rb, line 165
def get_time(&async_response_handler)
  @protocol.send_message(Endpoints::TIME, "\x00", async_response_handler) do |message|
    restype, timestamp = message.unpack("CL>")
    Time.at(timestamp)
  end
end
get_versions(&async_response_handler) click to toggle source
# File lib/pebble/watch.rb, line 106
def get_versions(&async_response_handler)
  @protocol.send_message(Endpoints::VERSION, "\x00", async_response_handler) do |message|
    response = {}

    response[:firmwares] = {}

    size = 47
    [:normal, :recovery].each_with_index do |type, index|
      offset = index * size + 1

      fw = {}

      fw[:timestamp], fw[:version], fw[:commit], fw[:is_recovery], 
        fw[:hardware_platform], fw[:metadata_version] =
        message[offset, size].unpack("L>A32A8ccc")

      fw[:is_recovery] = (fw[:is_recovery] == 1)

      response[:firmwares][type] = fw
    end

    response[:bootloader_timestamp], response[:hardware_version], response[:serial] =
      message[95, 25].unpack("L>A9A12")

    response[:btmac] = message[120, 6].unpack("H*").first.scan(/../).reverse.map { |c| c.upcase }.join(":")

    response
  end
end
listen_for_events() click to toggle source
# File lib/pebble/watch.rb, line 65
def listen_for_events
  @protocol.listen_for_messages
end
notification_email(sender, subject, body) click to toggle source
# File lib/pebble/watch.rb, line 95
def notification_email(sender, subject, body)
  notification(:email, sender, body, subject)
end
notification_sms(sender, body) click to toggle source
# File lib/pebble/watch.rb, line 91
def notification_sms(sender, body)
  notification(:sms, sender, body)
end
on_event(event = :any, &handler) click to toggle source
# File lib/pebble/watch.rb, line 69
def on_event(event = :any, &handler)
  @event_handlers[event] << handler
  handler
end
ping(cookie = 0xDEADBEEF, &async_response_handler) click to toggle source
# File lib/pebble/watch.rb, line 82
def ping(cookie = 0xDEADBEEF, &async_response_handler)
  message = [0, cookie].pack("CL>")

  @protocol.send_message(Endpoints::PING, message, async_response_handler) do |message|
    restype, cookie = message.unpack("CL>")
    cookie
  end
end
remove_app(app_id, app_index) click to toggle source
# File lib/pebble/watch.rb, line 159
def remove_app(app_id, app_index)
  message = [2, app_id, app_index].pack("cL>L>")

  @protocol.send_message(Endpoints::APP_INSTALL_MANAGER, message)
end
reset() click to toggle source
# File lib/pebble/watch.rb, line 187
def reset
  @protocol.send_message(Endpoints::RESET, "\x00")
end
set_nowplaying_metadata(artist, album, track) click to toggle source
# File lib/pebble/watch.rb, line 99
def set_nowplaying_metadata(artist, album, track)
  message = [16].pack("C")
  message << package_strings(artist, album, track, 30)

  @protocol.send_message(Endpoints::MUSIC_CONTROL, message)
end
set_time(time) click to toggle source
# File lib/pebble/watch.rb, line 172
def set_time(time)
  timestamp = time.to_i
  message = [2, timestamp].pack("CL>")

  @protocol.send_message(Endpoints::TIME, message)
end
stop_listening(*params) click to toggle source
# File lib/pebble/watch.rb, line 74
def stop_listening(*params)
  handler = params.pop
  event   = params.pop || :any

  @event_handlers[event].delete(handler)
end
system_message(code) click to toggle source
# File lib/pebble/watch.rb, line 179
def system_message(code)
  Pebble.logger.debug "Sending system message #{SystemMessages.for_code(code)}"

  message = [code].pack("S>")

  @protocol.send_message(Endpoints::SYSTEM_MESSAGE, message)
end

Private Instance Methods

answer_phone_version_message() click to toggle source
# File lib/pebble/watch.rb, line 192
def answer_phone_version_message
  @protocol.on_receive(Endpoints::PHONE_VERSION) do |message|
    response_message = [1, -1].pack("Cl>")
    response_message << [@session_capabilities, @client_capabilities].pack("L>L>")

    @protocol.send_message(Endpoints::PHONE_VERSION, response_message)
  end
end
log_log_events() click to toggle source
# File lib/pebble/watch.rb, line 216
def log_log_events
  on_event(:log) do |event|
    case event.level
    when :error
      Pebble.logger.error event.message
    when :warning
      Pebble.logger.warn event.message
    when :info
      Pebble.logger.info event.message
    when :debug, :verbose
      Pebble.logger.debug event.message
    else
      Pebble.logger.info event.message
    end
  end
end
notification(type, *params) click to toggle source
# File lib/pebble/watch.rb, line 249
def notification(type, *params)
  types = {
    email:  0,
    sms:    1
  }

  timestamp = Time.now.to_i
  params.insert(2, timestamp.to_s)

  message = [types[type]].pack("C") 
  message << package_strings(*params)

  @protocol.send_message(Endpoints::NOTIFICATION, message)
end
package_strings(*parts) click to toggle source
# File lib/pebble/watch.rb, line 264
def package_strings(*parts)
  max_part_length = 255
  max_part_length = parts.pop if parts.last.is_a?(Integer)

  message = ""
  parts.each do |part|
    part ||= ""

    part = part[0, max_part_length] if part.length > max_part_length
    message << [part.length].pack("C") + part
  end
  message
end
receive_event_messages() click to toggle source
# File lib/pebble/watch.rb, line 201
def receive_event_messages
  events = [
    [:log,            Endpoints::LOGS,            LogEvent],
    [:system_message, Endpoints::SYSTEM_MESSAGE,  SystemMessageEvent],
    [:media_control,  Endpoints::MUSIC_CONTROL,   MediaControlEvent]
  ]

  events.each do |(name, endpoint, event_klass)|
    @protocol.on_receive(endpoint) do |message|
      event = event_klass.parse(message)
      trigger_event(name, event) if event
    end
  end
end
trigger_event(name, event) click to toggle source
# File lib/pebble/watch.rb, line 233
def trigger_event(name, event)
  Pebble.logger.debug "Triggering event '#{name}': #{event.inspect}"

  @event_handlers[:any].each do |handler|
    Thread.new(handler) do |handler|
      handler.call(name, event)
    end
  end

  @event_handlers[name].each do |handler|
    Thread.new(handler) do |handler|
      handler.call(event)
    end
  end
end