#!/usr/bin/env ruby # # smc.rb # # Copyright (c) 2007 Naoki Hiroshima # You can redistribute it and/or modify it under the same terms as GPL2. # # Author:: Naoki Hiroshima # # Also see Fan Control # http://www.lobotomo.com/products/FanControl/ # require 'ioservice.rb' class ServiceManagementControl < IOService def open(&block) super("AppleSMC") end def read_keyinfo(key) i = alloc_structure key i[42] = 0x09 if o = structure_io(i) size = o[31] << 12 size += o[30] << 8 size += o[29] << 4 size += o[28] type = o[35].chr type += o[34].chr type += o[33].chr type += o[32].chr return {:size => size, :type => type} end end def read_raw(key) info = read_keyinfo(key) i = alloc_structure key i[28] = info[:size] i[42] = 0x05 if o = structure_io(i) a = [] info[:size].times do |i| a << o[48+i] end info[:raw] = a info[:value] = parse(info[:type], a) return info end {} end def read_value(key) read_raw(key)[:value] end def keys read_value('#KEY') || 0 end def key(index) i = alloc_structure i[42] = 0x08 i[44] = index if o = structure_io(i) return o.bytestr[0..3].reverse end nil end def each keys.times do |i| k = key(i) v = read_raw(k) yield k, v end end private def structure_io(i) o = alloc_structure sizeO = OSX::ObjcPtr.allocate_as_int32 sizeO.assign 80 super(2, 80, sizeO, i, o) == 0 ? o : nil end def alloc_structure(key=nil) i = super 80 if key i[0] = key[3] i[1] = key[2] i[2] = key[1] i[3] = key[0] end return i end def parse(type, value) case type when 'ch8*' value.pack('c*') when 'ui8 ', 'flag' value[0] when 'ui16' value.pack('C*').unpack('n*')[0] when 'ui32' value.pack('C*').unpack('N*')[0] when 'fpe2' value.pack('C*').unpack('n*')[0] / 4.0 when 'sp78' v = value.pack('C*').unpack('n*')[0] v -= 65536 if v > 32767 v / 256.0 else nil end end end if $0 == __FILE__ ServiceManagementControl.new.open do |smc| case ARGV[0] when 'all' smc.each do |k, v| puts "%s %s[%2d] %s ... %s" % [k, v[:type][0]>0 ? v[:type] : '----', v[:size], v[:value], v[:raw].inspect ] end else puts " CPU-1: %.2f C" % smc.read_value('TC0D') puts " CPU-2: %.2f C" % smc.read_value('TC0P') puts "Motion: X(%.3f) Y(%.3f) X(%.3f)" % ['MO_X','MO_Y','MO_Z'].map { |m| smc.read_value(m) } fans = smc.read_value('FNum') puts " Fans: #{fans}" fans.times do |i| cur = smc.read_value("F#{i}Ac") puts " %d: %.2f RPM" % [i, cur] end end end end