среда, 6 июля 2016 г.

Conky: мои настройки отображения погоды (yahooapis и jq) и музыки (cmus)

Оригинальные настройки conky я взял отсюда. В этот пакет, кроме собственно конфигураций окон, входят шрифты для отображения простого текста и погодных символов. К сожалению, в мае этого года окно weather_date перестало показывать погоду. Как выяснилось, это окно использует yahooapis, а Yahoo изменил свой API как раз в это время. В общем, пришлось переписать это окно для новых yahooapis, а заодно улучшить производительность за счет уменьшения вызовов процессов-фильтров, познакомиться с замечательным парсером JSON jq и перевести настройки в новый формат с помощью скрипта convert.lua, который поставляется вместе с обновленным conky. На следующей картинке показано, как выглядит окно weather_date на моих рабочем и домашнем компьютерах.
Запускать conky с окном weather_date нужно так:
conky -c ~/.grayscale/conkyrc/weather_date
А это сам скрипт weather_date.
conky.config = {
--###############
--###############PERFORMANCE_SETTINGS
--###############
    update_interval = 5,
    total_run_times = 0,
    net_avg_samples = 2,
    imlib_cache_size = 0,
    double_buffer = true,
    no_buffers = true,

--###############
--###############TEXT_SETTINGS
--###############
    use_xft = true,
    font = 'GE Inspira:bold:pixelsize=12',
    xftalpha = 0.1,
    override_utf8_locale = true,
    text_buffer_size = 512,

--###############
--###############WINDOW_SPECIFICATIONS
--###############
    background = true,
    own_window = true,
    own_window_transparent = true,
    own_window_type = 'normal',
    own_window_class = 'conky-semi',
    own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
    own_window_argb_visual = true,
    own_window_argb_value = 0,
    draw_outline = false,
--# Window border
    draw_borders = false,
    pad_percents = 0,
    border_inner_margin = 4,
    top_name_width = 10,
    use_spacer = 'right',
--#Size and position
    alignment = 'top_left',
    gap_x = 1600,
    gap_y = 58,
    minimum_width = 0, minimum_height = 0,
    maximum_width = 240,

--###############
--###############GRAPHICS_SETTINGS
--###############
    draw_shades = false,
    default_shade_color = '#292421',
    short_units = true,
--#Default Colors
    default_color = '#efefef',
    default_shade_color = '#1d1d1d',
--#Color Title
    color1 = '#bcbcbc',
    color2 = '#00d787',
    color3 = '#00d787',
};

conky.text = [[
#################
#################DATE & TIME
#################
${voffset 10}${font GE Inspira:pixelsize=50}${color1}${time %H:%M}\
${voffset -20}${offset 5}${font GE Inspira:pixelsize=25}${color2}${time %d}\
${voffset -15}${font GE Inspira:pixelsize=20}${color1}${time  %b}${time %Y}\
${voffset 35}${offset -105}${font GE Inspira:pixelsize=22}${color1}${time %A}\
${color}${font}
${voffset 5}${color3}${hr 2}${color}\

################
################DOWNLOADING WEATHER INFO AND SAVING IT AS ~/.cache/weather.json
################
${execi 1800 nm-online -t 60 && curl -s \
    -G 'http://query.yahooapis.com/v1/public/yql?format=json' \
    --data-urlencode 'q=select * from weather.forecast where woeid in \
    (select woeid from geo.places(1) where text="Saint-Petersburg, Russia") \
    and u="c"' -o ~/.cache/weather.json}
################MAIN WEATHER IMAGE
${voffset -10}${offset 20}${font conkyweather:size=140}${color1}\
${execi 1800 grep "^$(jq -r '.query.results.channel.item.condition.code' \
    ~/.cache/weather.json) =" ~/.grayscale/data/compare | cut -d " " -f3}\
${color}${font}
################WEATHER CONDITIONS
${alignc 10}${font GE Inspira:bold:pixelsize=15}${color2}\
${execi 1800 jq -r '.query.results.channel.item.condition.text' \
    ~/.cache/weather.json}${color}${font}
##############EXTRACTING CURRENT/HIGH TEMP IN DEGREE CELSIUS
${offset 30}${font GE Inspira:pixelsize=50}${color3}\
${execi 1800 jq -r '.query.results.channel.item.condition.temp' \
    ~/.cache/weather.json}°C/${font GE Inspira:pixelsize=30}\
${color3}${execi 1800 jq -r '.query.results.channel.item.forecast[0].high' \
    ~/.cache/weather.json}°C\
${color}${font}\

#################
#################EXTRACTING LOCATION
#################
${voffset 16}${offset 16}${font GE Inspira:bold:pixelsize=20}${color1}\
${execi 1800 jq -j '.query.results.channel.location | .city + ", ", .country' \
    ~/.cache/weather.json}${color}${font}
${color3}${hr 2}${color}\

#################
#################EXTRACTING WEATHER INFO
#################
##PRESSURE     HUMIDITY
${voffset 5}${font GE Inspira:bold:pixelsize=12}${color2}\
Pressure : ${color1}\
${execi 1800 jq -r '.query.results.channel.atmosphere.pressure' \
    ~/.cache/weather.json}mb\
${alignr -16}${font GE Inspira:bold:pixelsize=12}${color2}\
Humidity : ${color1}\
${execi 1800 jq -r '.query.results.channel.atmosphere.humidity' \
    ~/.cache/weather.json}%\
${color}${font}
##SUNRISE     SUNSET
${font GE Inspira:bold:pixelsize=12}${color2}\
Sunrise : ${color1}\
${execi 1800 jq -r '.query.results.channel.astronomy.sunrise' \
    ~/.cache/weather.json}\
${alignr -16}${font GE Inspira:bold:pixelsize=12}${color2}\
Sunset : ${color1}\
${execi 1800 jq -r '.query.results.channel.astronomy.sunset' \
    ~/.cache/weather.json}${color}${font}
##WIND     VISIBILITY
${font GE Inspira:bold:pixelsize=12}${color2}\
Wind : ${color1}${execi 1800 jq -r '.query.results.channel.wind.speed' \
    ~/.cache/weather.json}km/hr\
${alignr -16}${font GE Inspira:bold:pixelsize=12}${color2}\
Visibility : ${color1}\
${execi 1800 jq -r '.query.results.channel.atmosphere.visibility' \
    ~/.cache/weather.json}km${color}${font}
${color3}${hr 2}${color}\

#################
#################WEATHER FORECAST IMAGES FOR NEXT 2 DAYS
#################
${voffset 10}${font conkyweather:size=70}${color1}\
${execi 1800 grep "^$(jq -r '.query.results.channel.item.forecast[1].code' \
    ~/.cache/weather.json) =" ~/.grayscale/data/compare | cut -d " " -f3}\
${tab 72}${font conkyweather:size=70}${color1}\
${execi 1800 grep "^$(jq -r '.query.results.channel.item.forecast[2].code' \
    ~/.cache/weather.json) =" ~/.grayscale/data/compare | cut -d " " -f3}\
${color}${font}\

#################
#################EXTRACTING LOW/HIGH TEMP IN DEGREE CELSIUS FOR NEXT 2 DAYS
#################
${font GE Inspira:bold:pixelsize=15}${color2}\
${execi 1800 jq -r '.query.results.channel.item.forecast[1].day' \
    ~/.cache/weather.json} : ${font GE Inspira:bold:pixelsize=15}${color1}\
${execi 1800 jq -r '.query.results.channel.item.forecast[1].low' \
    ~/.cache/weather.json}°/${font GE Inspira:bold:pixelsize=15}${color1}\
${execi 1800 jq -r '.query.results.channel.item.forecast[1].high' \
    ~/.cache/weather.json}°\
${alignr -16}${font GE Inspira:bold:pixelsize=15}${color2}\
${execi 1800 jq -r '.query.results.channel.item.forecast[2].day' \
    ~/.cache/weather.json} : ${font GE Inspira:bold:pixelsize=15}${color1}\
${execi 1800 jq -r '.query.results.channel.item.forecast[2].low' \
    ~/.cache/weather.json}°/${font GE Inspira:bold:pixelsize=15}${color1}\
${execi 1800 jq -r '.query.results.channel.item.forecast[2].high' \
    ~/.cache/weather.json}°${color}${font}
${voffset 5}${color3}${hr 2}${color}\

#################
#################CALENDER DISPLAY
#################
${voffset 10}${font nimbus mono L:bold:size=12}${color1}\
${execpi 1800 DJS=`date +%_d`; cal | \
    sed -e s/"\(^\|[^0-9]\)$DJS"'\b'/'\1${color2}'"$DJS"'$color'/ -e s/^/'  '/}
]];
Внутри conky.config находятся настройки геометрии окна, объявляются различные цвета и т.п. Собственно содержимое окна описано внутри conky.text. В параграфе, озаглавленном как DOWNLOADING WEATHER …, каждые полчаса (1800 секунд) с помощью execi и curl, после предварительной проверки доступности сети командой nm-online, выполняется запрос на query.yahooapis.com в формате YQL (Yahoo! Query Language). Ответ от Yahoo сохраняется в файле ~/.cache/weather.json. Обратите внимание на то, что данная команда execi ничего не выводит на экран, то есть фактически в данном случае conky работает как cron! Я выбрал формат вывода JSON потому, что он легко и непринужденно парсится прямо из командной строки командой jq, которая позволяет задавать довольно сложные фильтры для поиска данных, предоставляя своеобразный язык запросов, который очень подробно, с примерами, описан в man jq. Все оставшиеся параграфы внутри conky.text вплоть до вывода календаря вызывают jq для поиска значений определенных погодных категорий внутри файла ~/.cache/weather.json. Например, внутри параграфа EXTRACTING LOCATION выполняется запрос jq -j '.query.results.channel.location | .city + ", ", .country' ~/.cache/weather.json, который выводит значения city и country, находящиеся в пути /query/results/channel/location структуры JSON и выводит их в одной строке (опция -j). Поскольку jq работает из командной строки, имеется возможность выводить погодные условия прямо на терминал! Например, погоду на завтра можно вывести командой
jq '.query.results.channel.item.forecast[1]' ~/.cache/weather.json
{
  "code": "12",
  "date": "07 Jul 2016",
  "day": "Thu",
  "high": "17",
  "low": "12",
  "text": "Rain"
}
При этом вывод в терминале будет синтаксически подсвечен! А теперь о выводе информации из cmus. Cmus — это аудиоплеер для терминала. В оригинальном пакете была представлена поддержка плеера clementine, но я им не пользуюсь. Информация из clementine выводилась в окне net_hdd с помощью execpi 2 ~/.grayscale/data/clementine, скрипт clementine в директории ~/.grayscale/data/ был написан на perl. Сначала я написал аналогичный скрипт cmus, тоже на perl, но в новом conky большое количество проблем заставило переписать его на lua и загружать в conky командой lua_load. Вот так выглядит информация из cmus, когда он запущен.
Для того, чтобы это заработало, в net_hdd, предварительно обработанном скриптом convert.lua, внутрь conky.config нужно добавить строку
        lua_load = '~/.grayscale/data/cmus.lua',
Внутрь conky.text, вместо настроек для clementine, нужно добавить строки
################
################CMUS_DISPLAY
################
${voffset 5}${offset 22}\
${font GE Inspira:bold:pixelsize=15}${color2}CMUS${voffset 2}\
${offset 5}${color3}${hr 2}${color}${font}
${if_running cmus}${lua_parse cmus}${else}${voffset 46}${endif}
Обратите внимание, что в случае, когда cmus не запущен, в окно net_hdd выводится пустой вертикальный сдвиг высотой 46 пикселей: это сделано для того, чтобы окно не мерцало при запуске и закрытии cmus. Величина сдвига была определена экспериментально: она должна точно соответствовать высоте совокупной информации из cmus, когда он запущен, выводимой conky. Кроме того, стоит отметить, что lua_parse, в отличие от execpi, не имеет собственной настройки интервала обновления данных, а следовательно скорость обновления данных определяется системной настройкой окна update_interval внутри conky.config. Скрипт cmus.lua в директории ~/.grayscale/data/ выглядит так.
function conky_echo(a)
    return a
end

function conky_cmus()
    local artist   = 'N/A';
    local title    = 'N/A';
    local album    = 'N/A';
    local progress = 0;
    local pos      = 0;
    local length   = 0;
    local status   = '';
    
    local color1   = 'bcbcbc';
    local color2   = 'ffa300';
    local color3   = 'ffff5f';
    
    f = assert( io.popen( 'cmus-remote -Q' ) ) or os.exit( 1 )
     
    for line in f:lines() do
        local v = string.match( line, '^status%s*(.*)' )
        if v ~= nil and v ~= '' and v ~= 'playing' then
            status = ' [' .. v .. ']'; goto next
        end
        v = string.match( line, '^duration%s*(.*)' )
        if v ~= nil and v ~= '' then length = tonumber( v ); goto next end
        v = string.match( line, '^position%s*(.*)' )
        if v ~= nil and v ~= '' then pos = tonumber( v ); goto next end
        v = string.match( line, '^tag album%s*(.*)' )
        if v ~= nil and v ~= '' then album = v; goto next end
        v = string.match( line, '^tag artist%s*(.*)' )
        if v ~= nil and v ~= '' then artist = v; goto next end
        v = string.match( line, '^tag title%s*(.*)' )
        if v ~= nil and v ~= '' then title = v; goto next end
        ::next::
    end
      
    f:close()
    
    if pos > 0 and length > 0 then
        progress = math.floor( 100 * pos / length )
    end
    
    return '${voffset 5}${offset 6}${font StyleBats:size=10}${color ' ..
            color1 .. '}k${voffset -2}${offset 3}${color ' .. color2 ..
            '}${font}Title: ${color ' .. color3 .. '}${alignr}' .. title ..
            '${color ' .. color2 .. '}' .. status ..
            '\n${offset 6}${font StyleBats:size=10}${color ' .. color1 ..
            '}k${voffset -2}${offset 3}${color ' .. color2 ..
            '}${font}Artist: ${color ' .. color3 .. '}${alignr}' .. artist ..
            '\n${offset 6}${font StyleBats:size=10}${color ' .. color1 ..
            '}k${voffset -2}${offset 3}${color ' .. color2 ..
            '}${font}Album: ${color ' .. color3 .. '}${alignr}' .. album ..
            '${color ' .. color1 .. '}${font}' ..
            '\n${voffset 1}${offset 8}${lua_bar echo ' .. progress .. '}'
end
Прошу меня простить, если что-то здесь сделано неизящно или неэффективно — это моя первая программа на lua :)

Комментариев нет:

Отправить комментарий