Мой трекер продуктивности — это «волшебное зеркало», но вместо времени, погоды и мотивационных цитат оно показывает:

  • Сколько времени я сегодня потратил на рабочие задачи за компьютером и в смартфоне (RescueTime).
  • Мой список дел из Trello.
  • «Радарный график», отображающий мои затраты времени на разные категории приложений по сравнению со вчерашним днём (RescueTime).
  • Недельные итоги (RescueTime).

Если вы потратили больше 50 % времени на рабочие задачи, вокруг зеркала будет яркая зелёная подсветка. Если меньше 50 % — подсветка будет красной, сигнализируя вам, что нужно повысить продуктивность! Впрочем, распределение вы можете задать самостоятельно.





Сначала я объясню суть трекера продуктивности. Затем приведу два разных списка инструментов и компонентов, так что вы сможете сделать полную или базовую (без светодиодной подсветки) версию трекера, не требующую умения паять. Дальше покажу, как делал прототип своего проекта, чтобы вам было легче прототипировать свой. Вы узнаете, как адаптировать код под свои нужды, и в конце рассмотрим отладку устройства.

Я не буду давать вам готовый рецепт, я лишь хочу показать способ создания трекера и задать вам вопросы, в поисках ответов на которые вы сможете привнести свои идеи, развив проект.

Идея




Я несколько месяцев собирался написать эту статью. Но мне этого не позволял мой старый друг по имени «Прокрастинация». Я постоянно откладывал написание, заполняя своё время просмотром видео из тёмных глубин YouTube.

Однажды я читал статью о прокрастинации и узнал про сервисы, которые могли бы помочь в решении моей проблемы: диспетчер задач Trello и планировщик времени RescueTime. Оба прекрасно подошли под мои нужды, но через какое-то время я перестал заходить на их страницы и начал игнорировать уведомления. И тогда я пришёл к идее своего проекта: хочу отображать данные из приложений на зеркало, висящее на стене. Тогда я точно не спрячусь от своих обязанностей.

Первый набросок проекта был простым: буду использовать API RescueTime API для отображения моей продуктивной/отвлекающей деятельности, а API Trello API — для отображения списка задач на день. Но позднее я добавил и другие возможности, но об этом ниже.

Инструменты и компоненты


Эти инструменты я использовал для создания своего трекера, ваш набор может быть другим!

Для рамы:

  • 4 метра фанеры 7 x 15 см — зависит от размера вашего монитора.
  • Одностороннее зеркало 30 x 40 см — зависит от размера вашего монитора.
  • Монитор 25 x 35 см — нашёл на блошином рынке. Можете использовать любой.
  • Raspberry Pi 3 Model B — подойдёт и Raspberry Pi 2, но придётся найти Wi-Fi-модуль.
  • Кабель HDMI.

Для светодиодной подсветки:

  • Двухметровая RGB-светодиодная лента smd5050 — нужна именно smd5050, причину объясню ниже.
  • Три N-канальных мосфета (например, IRLZ34N).
  • Блок питания 12 В — 2 A.
  • Макетная плата.

Инструменты:

  • Пила по дереву.
  • Клей для дерева.
  • Паяльные принадлежности (для светодиодной подсветки).

Код




Эта глава состоит из трёх частей, вам не нужны все перечисленные выше компоненты, пока достаточно лишь Raspberry Pi и монитора.

Настройка Raspberry Pi


Если вы ещё не настроили Raspberry Pi, сделайте это. Для проекта вам понадобится установить веб-сервер Apache и PHP-LED-контроллер.

Руководство по установке Apache: https://www.raspberrypi.org/documentation/remote-access/web-server/apache.md.

Руководство по установке PHP-LED-контроллера: https://github.com/k1sul1/Raspberry-Pi-PHP-LED-controller.

Теперь из репозитория проекта скачайте на вашу Raspberry Pi файл index.php и положите его сюда: /var/www/html/

Если не знаете, как перемещать файлы с помощью терминала Linux, почитайте здесь: https://www.linux.com/learn/how-move-files-using-linux-commands-or-file-managers.

Адаптация кода


Здесь потребуются небольшие знания HTML/CSS.

Мы будем адаптировать файл index.php, являющийся сердцем проекта. Подключите монитор к Raspberry Pi. Если попытаетесь открыть index.php, то ничего не выйдет, потому что сначала нужно вставить в код API-ключи. Для этого войдите в аккаунт на сайте RescueTime, перейдите в раздел для разработчиков и нажмите Activate This Key. Сохраните куда-нибудь этот ключ.

То же самое сделайте и для API Trello: войдите в аккаунт и на портале для разработчиков сгенерируйте API-ключ.

Теперь откройте в своём текстовом редакторе файл index.php, лежащий в /var/www/html/, и замените [API_KEY] на ключи из RescueTime и Trello. [list_number] — номер списка, который нужен для получения списка задач из Trello. Чтобы сгенерировать номер, сначала создайте в Trello свежий список и назовите «To-Do», задачи из этого списка будут отображаться на волшебном зеркале.

Теперь скопируйте из строки браузера адрес, который выглядит так:

https://trello.com/b/3hS6yyLo/board-name

добавьте в конце .json: https://trello.com/b/3hS6yyLo/board-name.json
нажмите Enter, и экран заполнится кодом. Найдите в этом хаосе имя вашего списка ''To-Do". Это должно выглядеть подобным образом: {"name":"To Do","id":"5981c123cd1b23f13907cd18"}, здесь id — идентификатор вашего списка. Вставьте его значение в [list_number] в файле index.php.

Теперь откройте браузер, в адресной строке напишите localhost и нажмите Enter. На графиках должны отобразиться ваши данные.

Примечание: местоположение графиков может отличаться из-за разрешения вашего монитора. Настроить ширину, высоту и расположение элементов можно в CSS-разделе кода.

Теперь осталось сделать рамку для монитора и подключить светодиоды.

Примечание: если вас не интересует подробное рассмотрение API, можете сразу перейти к следующей главе.

API в подробностях


В основе проекта лежат два API:


Хотя в документации есть вся нужная информация, я объясню, какие данные из API использует наш проект.

В разделе управления временем вызывается RescueTime API и запрашивается информация о сегодняшнем распределении времени:

"https://www.rescuetime.com/anapi/data?key=[API_KEY]&perspective=rank&interval=hour&restrict_begin=".date('Y-m-d')."&restrict_end=".date('Y-m-d')."&format=json"

Здесь
date('Y-m-d') — текущая дата.
perspective=rank — вид сортировки данных. В данном случае по «категории», то есть на что потрачено больше всего времени.

После выполнения вызова мы получаем файл в JSON-формате (см. код файла data.json в конце главы). Из него мы возьмём «Time Spent (seconds)» и «Productivity» (может иметь значения от -2 (отвлекаешься) до 2, (продуктивен)). На основе этих данных можно сгенерировать индекс продуктивности со значением больше 100.

Следующий вызов API RescueTime:

"https://www.rescuetime.com/anapi/daily_summary_feed?key=[API_KEY]"

Вы получите недельный отчёт (см. код файла summary.json в конце главы). Эти данные используются для построения недельного графика.

Вызов Trello API:

"https://api.trello.com/1/lists/[list_number]/cards?fields=name&key=[API_KEY]&token=[Token]"

Вы получите данные из карточек задач в Trello:

[{"id":"5a4160103bfcd14994852f59","name":"ceylan cinemagraph"},{"id":"59e8241f6aa8662a51eb7de6","name":"Learn GitHuB"},{"id":"5981c19577c732f826ad8025","name":"Publish Instructible"},{"id":"5a341dba7f17d235d7c5bbd1","name":"SPACE PROGRAM"}]

В карточках вы можете размещать текст и передавать его ещё куда-нибудь.

data.json

Код
Formatted JSON Data
{  
   "notes":"data is an array of arrays (rows), column names for rows in row_headers",
   "row_headers":[  
      "Rank",
      "Time Spent (seconds)",
      "Number of People",
      "Activity",
      "Category",
      "Productivity"
   ],
   "rows":[  
      [  
         1,
         1536,
         1,
         "en.0wikipedia.org",
         "Uncategorized",
         0
      ],
      [  
         2,
         1505,
         1,
         "youtube.com",
         "Video",
         -2
      ],
      [  
         3,
         1178,
         1,
         "OpenOffice",
         "Writing",
         2
      ],
      [  
         4,
         709,
         1,
         "moodle.bilkent.edu.tr",
         "General Reference \u0026 Learning",
         2
      ],
      [  
         5,
         602,
         1,
         "google.com.tr",
         "Search",
         2
      ],
      [  
         6,
         439,
         1,
         "reddit.com",
         "General News \u0026 Opinion",
         -2
      ],
      [  
         7,
         437,
         1,
         "tr.sharelatex.com",
         "Writing",
         2
      ],
      [  
         8,
         361,
         1,
         "yemeksepeti.com",
         "General Shopping",
         -2
      ],
      [  
         9,
         356,
         1,
         "Gmail",
         "Email",
         0
      ],
      [  
         10,
         328,
         1,
         "Google Chrome",
         "Browsers",
         0
      ],
      [  
         11,
         207,
         1,
         "stars.bilkent.edu.tr",
         "General Reference \u0026 Learning",
         2
      ],
      [  
         12,
         179,
         1,
         "whatsapp",
         "Instant Message",
         -1
      ],

summary.json

Код
[  
   {  
      "id":1515657600,
      "date":"2018-01-11",
      "productivity_pulse":54,
      "very_productive_percentage":34.2,
      "productive_percentage":10.6,
      "neutral_percentage":25.6,
      "distracting_percentage":0.0,
      "very_distracting_percentage":29.6,
      "all_productive_percentage":44.8,
      "all_distracting_percentage":29.6,
      "uncategorized_percentage":16.1,
      "business_percentage":6.0,
      "communication_and_scheduling_percentage":4.3,
      "social_networking_percentage":0.0,
      "design_and_composition_percentage":0.0,
      "entertainment_percentage":15.2,
      "news_percentage":3.3,
      "software_development_percentage":5.4,
      "reference_and_learning_percentage":22.8,
      "shopping_percentage":12.9,
      "utilities_percentage":14.1,
      "total_hours":2.51,
      "very_productive_hours":0.86,
      "productive_hours":0.27,
      "neutral_hours":0.64,
      "distracting_hours":0.0,
      "very_distracting_hours":0.74,
      "all_productive_hours":1.12,
      "all_distracting_hours":0.74,
      "uncategorized_hours":0.4,
      "business_hours":0.15,
      "communication_and_scheduling_hours":0.11,
      "social_networking_hours":0.0,
      "design_and_composition_hours":0.0,
      "entertainment_hours":0.38,
      "news_hours":0.08,
      "software_development_hours":0.13,
      "reference_and_learning_hours":0.57,
      "shopping_hours":0.32,
      "utilities_hours":0.35,
      "total_duration_formatted":"2h 30m",
      "very_productive_duration_formatted":"51m 26s",
      "productive_duration_formatted":"15m 56s",
      "neutral_duration_formatted":"38m 34s",
      "distracting_duration_formatted":"no time",
      "very_distracting_duration_formatted":"44m 30s",
      "all_productive_duration_formatted":"1h 7m",
      "all_distracting_duration_formatted":"44m 30s",
      "uncategorized_duration_formatted":"24m 11s",
      "business_duration_formatted":"9m 6s",
      "communication_and_scheduling_duration_formatted":"6m 26s",
      "social_networking_duration_formatted":"no time",
      "design_and_composition_duration_formatted":"no time",
      "entertainment_duration_formatted":"22m 49s",
      "news_duration_formatted":"4m 55s",
      "software_development_duration_formatted":"8m 3s",
      "reference_and_learning_duration_formatted":"34m 17s",
      "shopping_duration_formatted":"19m 22s",
      "utilities_duration_formatted":"21m 17s"
   },
   {  
      "id":1515571200,
      "date":"2018-01-10",
      "productivity_pulse":33,
      "very_productive_percentage":21.9,
      "productive_percentage":2.3,
      "neutral_percentage":14.4,
      "distracting_percentage":11.0,
      "very_distracting_percentage":50.3,
      "all_productive_percentage":24.2,
      "all_distracting_percentage":61.4,
      "uncategorized_percentage":0.3,
      "business_percentage":0.0,
      "communication_and_scheduling_percentage":13.5,
      "social_networking_percentage":0.0,
      "design_and_composition_percentage":6.3,
      "entertainment_percentage":44.7,
      "news_percentage":4.2,
      "software_development_percentage":0.0,
      "reference_and_learning_percentage":15.5,
      "shopping_percentage":0.0,
      "utilities_percentage":15.4,
      "total_hours":2.24,
      "very_productive_hours":0.49,
      "productive_hours":0.05,
      "neutral_hours":0.32,
      "distracting_hours":0.25,
      "very_distracting_hours":1.13,
      "all_productive_hours":0.54,
      "all_distracting_hours":1.37,
      "uncategorized_hours":0.01,
      "business_hours":0.0,
      "communication_and_scheduling_hours":0.3,
      "social_networking_hours":0.0,
      "design_and_composition_hours":0.14,
      "entertainment_hours":1.0,
      "news_hours":0.09,
      "software_development_hours":0.0,
      "reference_and_learning_hours":0.35,
      "shopping_hours":0.0,
      "utilities_hours":0.34,
      "total_duration_formatted":"2h 14m",
      "very_productive_duration_formatted":"29m 22s",
      "productive_duration_formatted":"3m 8s",
      "neutral_duration_formatted":"19m 18s",
      "distracting_duration_formatted":"14m 48s",
      "very_distracting_duration_formatted":"1h 7m",
      "all_productive_duration_formatted":"32m 30s",
      "all_distracting_duration_formatted":"1h 22m",
      "uncategorized_duration_formatted":"27s",
      "business_duration_formatted":"1s",
      "communication_and_scheduling_duration_formatted":"18m 5s",
      "social_networking_duration_formatted":"no time",
      "design_and_composition_duration_formatted":"8m 30s",
      "entertainment_duration_formatted":"59m 54s",
      "news_duration_formatted":"5m 39s",
      "software_development_duration_formatted":"no time",
      "reference_and_learning_duration_formatted":"20m 51s",
      "shopping_duration_formatted":"no time",
      "utilities_duration_formatted":"20m 39s"
   },
   {  
      "id":1515484800,
      "date":"2018-01-09",
      "productivity_pulse":68,
      "very_productive_percentage":60.4,
      "productive_percentage":0.5,
      "neutral_percentage":11.0,
      "distracting_percentage":7.1,
      "very_distracting_percentage":21.0,
      "all_productive_percentage":60.9,
      "all_distracting_percentage":28.1,
      "uncategorized_percentage":9.1,
      "business_percentage":21.9,
      "communication_and_scheduling_percentage":7.2,
      "social_networking_percentage":5.1,
      "design_and_composition_percentage":1.2,
      "entertainment_percentage":1.6,
      "news_percentage":12.5,
      "software_development_percentage":9.1,
      "reference_and_learning_percentage":28.2,
      "shopping_percentage":2.9,
      "utilities_percentage":1.2,
      "total_hours":2.78,
      "very_productive_hours":1.68,
      "productive_hours":0.01,
      "neutral_hours":0.31,
      "distracting_hours":0.2,
      "very_distracting_hours":0.58,
      "all_productive_hours":1.69,
      "all_distracting_hours":0.78,
      "uncategorized_hours":0.25,
      "business_hours":0.61,
      "communication_and_scheduling_hours":0.2,
      "social_networking_hours":0.14,
      "design_and_composition_hours":0.03,
      "entertainment_hours":0.04,
      "news_hours":0.35,
      "software_development_hours":0.25,
      "reference_and_learning_hours":0.78,
      "shopping_hours":0.08,
      "utilities_hours":0.03,
      "total_duration_formatted":"2h 46m",
      "very_productive_duration_formatted":"1h 40m",
      "productive_duration_formatted":"47s",
      "neutral_duration_formatted":"18m 23s",
      "distracting_duration_formatted":"11m 49s",
      "very_distracting_duration_formatted":"34m 57s",
      "all_productive_duration_formatted":"1h 41m",
      "all_distracting_duration_formatted":"46m 46s",
      "uncategorized_duration_formatted":"15m 7s",
      "business_duration_formatted":"36m 26s",
      "communication_and_scheduling_duration_formatted":"11m 59s",
      "social_networking_duration_formatted":"8m 28s",
      "design_and_composition_duration_formatted":"2m 4s",
      "entertainment_duration_formatted":"2m 39s",
      "news_duration_formatted":"20m 49s",
      "software_development_duration_formatted":"15m 5s",
      "reference_and_learning_duration_formatted":"46m 59s",
      "shopping_duration_formatted":"4m 51s",
      "utilities_duration_formatted":"2m 3s"
   }
  ]

Прототипирование


image





Данные теперь выводятся на монитор, пришла пора спроектировать рамку и зеркало. Здесь всё зависит от ваших предпочтений. Я взял светлую древесину, потому что она больше подходит к моему интерьеру.

Если вы хотите выбрать другую конструкцию рамы, то есть несколько отличных руководств:

https://www.instructables.com/id/Smart-Mirror-by-Raspberry-Pi/
https://www.instructables.com/id/Raspberry-Pi-Smart-Mirror/
https://www.instructables.com/id/Magic-Mirror/

Размеры заготовок для моей рамы:

Передняя сторона:

  • 2 x 40 см
  • 2 x 47 см

Задняя сторона:

  • 2 x 38 см
  • 2 x 50 см

Сборка рамы и зеркала


image





Некоторые замечания:

  • Для сборки рамы я использовал клей для дерева. После установки зеркала в раму дополнительно вклеил в углах деревянные треугольники, фиксирующие зеркало. Но потом я понял, что это было не лучшим решением, поскольку из-за треугольников рама не прижимается к стене.
  • Прежде чем склеивать детали, соберите всё на сухую и проверьте, нет ли зазоров вокруг монитора или других недочётов.

Сборка электроники


















Если делаете базовую версию без светодиодов, можете пропустить соответствующий этап.

Схемы сборки и установки светодиодов взяты из двух разных руководств:

How to control a RGB LED-Strip with a Raspberry Pi
Raspberry Pi PHP LED controller

Важные замечания:

Я использовал такие пины:

  • GPIO17 для красного провода.
  • GPIO22 для зелёного провода.
  • GPIO24 для синего провода.

Если хотите использовать другие пины, придётся изменить их номера в начале index.php.

Проверка




Всё должно работать нормально, но для запуска браузера и ввода localhost понадобятся мышь и клавиатура.

1. Запустите терминал и введите: sudo nano /home/pi/.config/lxsession/LXDE-pi/autostart
2. Затем введите: chromium-browser --kiosk --incognito "http://localhost/index.php"

Теперь можно отключить мышь и клавиатуру. Браузер должен запуститься и открыть файл index.php.

Устранение неисправностей


У меня ошибка API
У наших API есть ограничения на количество обращений. Если дёргать их слишком часто, получите ошибку. Для решения этой проблемы добавьте в index.php задержку обновления, по умолчанию стоит 360 секунд.

#Seconds to refresh the webpage<br>$sec = "360";

Браузер не открывается при загрузке
В разных ОС файл autostart находится в разных местах. Я использовал Raspbian GNU/Linux 8.0 Jessie, так что уточните, где лежит этот файл в вашей ОС.

Светодиоды светятся разным цветом
Подключение светодиодных лент может различаться, и наверняка цветовые подключения перемешаны. Исправить это можно с помощью припайки проводов к правильным контактам. Также можете подстроить цвета подсветки с помощью HEX-значений в index.php.

if ($oran<50) { $led->setHex("#FF0000"); }else { $led->setHex("#00FF00"); }

Вот вам в помощь HEX-калькулятор цветов.

Хочу использовать другие графики
Я применил JS-библиотеку chart.js. Если хотите использовать другие графики, почитайте документацию: http://www.chartjs.org/samples/latest/

Что дальше


Есть много способов улучшения этого проекта. Например:

  • Экран побольше: тогда можно выводить гораздо больше графиков, или оставить часть площади именно под зеркало.
  • Интеграция разных API: сегодня доступно огромное количество всевозможных API (Twitter, Google и множество других). Мне пришла в голову идея со Spotify API, чтобы отображать на зеркале, какая песня сейчас играется. Вот вам список для мозгового штурма: www.programmableweb.com/apis/directory
  • Голосовое управление Amazon Alexa: у меня стоит такое устройство, могу с помощью голоса управлять светом. Можно прикрутить Алексу для управления графиками на зеркале.

Комментарии (7)


  1. LeoPoldGR
    17.04.2018 16:07

    Очень круто.


  1. Terranz
    17.04.2018 16:53

    Спасибо за пример положительного опыта использования трекеров. В соседней теме было длинное обсуждение, нужно и\или полезно ли использование трекеров в работе.
    У вас отличный пример, спасибо ещё раз.


    1. Barrayar Автор
      17.04.2018 22:51

      Рад, что понравилось. Удачной реализации!


  1. springimport
    18.04.2018 16:10

    Как называется пятиугольник справа на первой картинке?


    1. Barrayar Автор
      18.04.2018 17:07

      Радарная диаграмма.


      1. springimport
        18.04.2018 18:09

        Спасибо.


  1. Disbeleiver
    19.04.2018 04:38

    Пытаюсь понять, почему нельзя получать эту информацию в каком-то из двух дисплеев, и так стоящих перед носом. Размер шрифта на дополнительном дисплее довольно мелкий и явно трудночитаем с позиций, отличных от «в кресле за столом», следовательно можно и в мониторах на столе это посмотреть.