Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
21.01.2024
HomeAssistant та керування через ІЧ
Пару років тому хотів автоматизувати кондиціонери, щоб вони вмикались за датчиками температури — в тому числі й на режим обігріву. Кондиціонери розумом не відрізняються та мають звичайні пульти.
Я спочатку шукав якусь хитру шину комунікації та, відповідно, пристрій, яким можна додати до кондиціонера “розумність”. Виявилося все набагато простіше. В кондиціонері з пультом пульт зберігає всі налаштування, та відсилає їх в кондиціонер пачкою інфрачервоних сигналів. Тож, за наявністю ІЧ передавача можна легко запускати будь-який режим та навіть без зворотного каналу комунікації з кондиціонером. (Насправді в пульту теж немає зворотного каналу, тож він прекрасно “працює” навіть коли поруч немає кондиціонерів — просто оновлює свій внутрішній стан та відправляє ІЧ-команди.)
Далі, як підʼєднати до HomeAssistant ІЧ передавач? В мене вийшло з ще одним розумним пристроєм - Broadlink Universal Remote. Це практично шлюз між Wi-Fi та ІЧ. Залишається змонтувати цю коробочку в місці де вона бачить кондиціонер — та додати в HomeAssistant.
Тепер, про інфрачервоні команди. Кожна команда це послідовність сигналів. Щоб відправляти команди, особливо такі складні, як стан кондиціонера, потрібно їх знати. В теорії, є готові бази команд — можна знайти на GitHub - але, як на мене, легше записати їх власноруч. Як — написано в інструкції по інтеграції. Доведеться трохи підредагувати файли конфігурації, але потім просто направляєш справжній пульт на Broadlink, натискаєш кнопку та команда зберігається.
Перевага HomeAssistant в тому, що він здатний повʼязати абсолютно різні пристрої, та ще й зі складною логікою, та ще й в дружній для інженера формі.
Отак. А простіші обігрівачі можна контролювати через розумну розетку, тут ніяких пультів не потрібно. Тільки підібрати розетку, щоб по потужності підходила. На кшталт такої.
20.01.2024
Як в iOS застосунок керує відтворенням звуків
Таємний проєкт застосунку iOS про відтворення картинок отримав трошки уваги. В результаті я дещо вивчив про аудіомодель iOS та виправив неприємний момент UX.
Річ у тім, що я відтворюю GIF через відеопрогравач. Вбудованого, хорошого способу відтворення GIF взагалі немає. Зрозуміло, що GIF це половина мемів та іншого контенту, тож застосунок повинний не просто показувати GIF, а ще й робити це зручно — з перемоткою та іншим. Найпростіший спосіб цього досягнути — конвертувати GIF у MP4 та відтворити звичайним програвачем.
Але виникла проблема: програвач, навіть без аудіодоріжки, зупиняв відтворення будь-якого іншого аудіо на пристрої: музики, подкасту і таке інше. Хотілося б дивитися GIFки під улюблений подкаст, тож, що з цим можна зробити?
Як видається, в iOS для того є низка режимів відтворення аудіо, які конфігуруються модулем AVAudioSession. Цей модуль тільки надає системі метаінформацію про характер аудіо, який потрібний нашому застосунку. Саме відтворення працює й без нього — через програвач або іншим шляхом. Тому, думаю, коли зустрічаєш застосунок, який погано поводиться з аудіо, то автори просто не знали про AVAudioSession, як і я.
Для мого випадку, підійшла категорія відтворення ambient: вона значить, що звук з мого застосунку можна змішувати з іншими програмами. Інші режими вказують, чи треба ставити відтворення на паузу при замкненому екрані, чи впливає на нього тихий режим, і так далі. Ніколи не думав, що з аудіо все настільки неоднозначно.
19.01.2024
Тобі потрібна директорія-пісочниця
В мене є окрема директорія, куди потрапляють разові скрипти та таке інше. Вона називається playground, та зазвичай вона відкрита в окремому вікні VS Code.
Раніше скрипти писались там, де вийде — інколи в директорії проєкту, інколи просто в довільному місці - “Документи” чи “Завантаження”, чи “Робочий стіл”. В моменті з цим проблем немає — аби працювало.
Проте стає питання — що робити з тими скриптами потім? Додавати до проєкту — не завжди доцільно; бо це робить скрипт частиною спільного коду. Видаляти — ну вони місця не займають, та завжди згодяться в майбутньому. Отже, одного дня я створив свою пісочницю та почав складати все туди.
В сталої директорії є ще й такі переваги, що можна мати локальні залежності, конфігурацію, ресурси. Особливо такі залежності, які в проєкт не вписуються, як і скрипт.
…А потім, через пів року після того, як “разовий” скрипт відпрацював та був забутим, зʼявляється потреба написати його ще раз. А він вже написаний! І готовий до виконання. Приємно!
18.01.2024
Docker як обгортка для команд
Зазвичай ми дивимось на Docker як на квазівіртуальну машину зі якимось сталим процесом всередині. Проте за бажанням в Docker можна запустити і єдину команду, причому ззовні це мало відрізняється від іншої консольної команди.
Навіщо таке може бути потрібне? Насправді багато є варіантів, які зводяться до запуску програм в оточенні Linux, та, можливо, архітектури процесора, що відрізняється від рідної. З огляду на те, що на серверах у всіх Linux, а на робочих машинах — переважно не Linux, це досить корисна можливість.
Наприклад, через Docker дуже легко виконати кроскомпіляцію коду. В деяких випадках не тільки легко, а і єдиний можливий випадок — наприклад, з macOS в Linux зі зміною архітектури. Або є деякий пакет для Linux, в якого є утиліта, яка потрібна на робочій машині — таке теж легко.
До контейнера можна примонтувати всі файли та директорії, які потрібні, причому Docker не буде їх копіювати, а зробить доступними через віртуальну файлову систему (яка, маю признати, на macOS працює трохи повільніше, ніж нормальна.)
Вся команда виглядає так (тут опція --rm видаляє контейнер по завершенню, а решта, сподіваюсь, зрозуміла:)
docker run --rm \
--mount type=bind,source='$(shell pwd)',target=/mnt \
--mount type=bind,source='$(shell go env GOCACHE)',target=/tmp/gocache \
--env GOCACHE=/tmp/gocache \
buildpack-deps:latest \
make -C src/tool
Скажу більше, в мене така команда сидить всередині if, який перевіряє: якщо ми на CI на Linux, то можна просто запустити make локально; а якщо на macOS з Apple Silicon - тоді в Docker. Економно — наскільки це можливо — та прозоро для користувача.
17.01.2024
Хороша історія в Git - моя практика
Як вчора писав, в мене завжди є (неявна) задача створити в Git таку історію, з якої легко буде пізніше (інколи значно пізніше) зрозуміти причину змін.
-
Одиницею осмислених змін є пул-реквест. Це значить, що найліпше, коли пул-реквест складається з одного коміту. Що там було всередині — втрачає сенс на широкому часовому масштабі. Пул-реквест гарний тим, що в нього є визначений автор, ціль, обговорення.
-
Всередині пул-реквесту, в нормі, має бути один коміт. Втім, тут є винятки. Перший виняток — коли пул-реквест складається з декількох логічних кроків, які вирішили не розбивати на декілька окремих пул-реквестів. Наприклад, ми вмикаємо та виправляємо по черзі 5 правил лінтера.
-
Другий виняток — якщо пул-реквест містить “побічні зміни”. Наприклад, окрім власне нового функціоналу я виправив баг в пакеті тестів. Знов-таки на окремий пул-реквест воно не варте та інколи навіть є перевага від того, щоб розташувати зміни в поєднанні, щоб в майбутньому помітити цей звʼязок. Бо два пул-реквести зі змінами в різних місцях буде важко повʼязати в майбутньому.
-
Щоб досягти хоч якоїсь організації комітів, необхідно використовувати git rebase. Бо безумовно комітити потрібно набагато раніше, ніж буде готовий весь пакет змін. В межах однієї гілки
git rebase(зазвичай) безконфліктний та безпечний інструмент. -
Поки гілка ще в роботі, коміти зберігають короткочасну історію змін, що теж корисно. Але коли робота завершена, короткочасна історія витрачає користь — тож під час формування пул-реквесту я роблю
rebaseта ліплю історію на майбутнє.
16.01.2024
Мій підхід до історії Git
Окрім того, що Git - незамінна платформа координації змін від всієї команди (а я ще памʼятаю “бронювання” файлів у Visual SourceSafe) - є й друга функція. Git - то правдива історія розробки проєкту. Та якщо про координацію змін багато всього написано — всілякі системи гілкування і таке інше, то облік історії не така гучна тема.
Навіщо взагалі потрібна історія? Не для пустих міркувань. Кожного разу, коли я натрапляю на незрозумілий код — я дивлюся на історію його змін. Незрозумілий код зустрічається постійно — під час планування, рефакторингу, пошуку багів. Історія, у формі git blame та git log, розкриває його в новому вимірі.
Замість злагодженої архітектури, в git blame код виглядає як латки, поставлені на латки, поставлені на латки… і так до самої основи. Та коли йдеться про незрозумілий код, то часто легше побачити структуру, коли знаєш, які зміни відбувались одночасно.
Треба дізнатись, яка з форм нова, а яка застаріла? В яких ще місцях зробили — або забули зробити — рефакторинг? Що хотів досягнути автор, коли впровадив помилку? Історія підкаже.
Тому важливо піклуватися про чисту історію. Я вже зачіпляв цю тему, але детальніше розповім завтра.
15.01.2024
Звичайні задачі мають прості рішення
🦪🦪🦪 Звичайні речі, найшвидше, будуть простими, а не складними. Варто згадати про це кожного разу, коли ти вдаєшся до ускладненого розвʼязка. Хаки мають власне місце та дозволяють подекуди досягнути нових, незапланованих результатів. Але погано, коли хак заміняє розуміння про правильне використання.
Уявимо, що ми забули, як працює змив в туалеті. Прямолінійне рішення знайти легко — носити черпаки води з рукомийника. Проте далі ми вивчаємо унітаз та знаходимо бачок з водою. Як зручно! Тепер можна черпати воду прямо з бачка, не потрібно бігати до рукомийника. Та хто ж це придумав так, що бачок прихований і залізти в нього нелегко? Ми інженери, знайдемо, що робити: приберемо кришку бачка, і ось він — наш не дуже красивий, але принаймні дієвий хак: черпак в бачку. Проблема вирішена.
Абсурд? Можливо. Тільки я багато бачив програм, яки мають не більше глузду, коли знаєш правильний підхід. Та більшість наших задач не є унікальною, а повторює чи нагадує те, що роблять інші.
Отже, перед тим, як робити хаки, визнач для себе: чи не задача звичайною? Тобто такою, яка стає перед більшістю людей та входить в “нормальне” використання предметного матеріалу? Якщо так — замість хаку краще перерити документацію та форуми зі впевненістю, що ускладнень можна уникнути.
14.01.2024
Три місяці з окремим компʼютером для роботи
З мого останнього поста на цю тему пройшло три місяці та одна відпустка. Забіжу наперед і скажу, що це була найкраща відпустка за декілька років.
З технічного боку, все досі працює чудово. Мені не потрібні ніякі KVM для перемикання між машинами, коли можна просто фізично перемкнути єдиний USB-C кабель, який йде до доку (CalDigit TS3+). До доку підʼєднані клавіатура, миша, монітор, Ethernet, колонки, вебкамера — і все це швидко та безпроблемно перемикається.
В теорії, можна досягти схожого результату й з одною машиною, якщо створити на ній додаткового користувача. Бо головна вигода від окремого компʼютера — це можливість миттєво перемикнути контексти — як туди, так і назад. Наприклад, увечері залишити сервіси запущеними в Docker, а вранці повернутись до них з повним збереженням стану.
Поки не наважився створити для роботи окремий GitHub. Для мене як для програміста виходить так, що саме акаунт в GitHub є головною “інтернет-особою”, та створювати новий, порожній якось.. неправильно. Втім, скоріше за все й до того дойду. Технічно ж майже немає різниці, а на рівні Git так й зовсім.
Психологічно, все ж надзвичайно задоволений відокремленням роботи та дозвілля. Мати відпустку без відволікання на робочі чати це незрівнянно. Раніше так вдавалося тільки якщо подорожувати без девайсів, що, будемо чесними, створює більше дискомфорту, ніж комфорту.
До речі, щоб цього досягти, я також виділив окремий робочий смартфон, бо з робочим Слаком в смартфоні все одно ж підглядаєш постійно. А так ще й нагода погратися з додатковим смартфоном.
Та, звісно, з іншого боку, на роботі легше сфокусуватися, коли на машині є тільки робота. Причому як в моменті, так і протягом дня — легко зрозуміти, чи я “на роботі”, чи “вдома”.
13.01.2024
Огляд фітнес-кільця Oura
З місяць тому мені подарували кільце Oura. Я, власне, нічого революційного від нього не очікував. Наразі я користуюся Apple Watch, та з нарікань до нього хіба те, що в годиннику незручно спати. Але виявилося, що в Oura цікава ментальна модель, яка відрізняється від всього, що я бачив (а бачив я Apple, Fitbit та Garmin). Вона задумана для звичайної людини зі звичайним життям, а не тільки для спортсменів.
Головне те, що Oura пропонує коучінг замість обліку. Набір метрик тут більш-менш такий самий, як з Apple Watch. Але застосунок Oura обчислює з них ряд похідних метрик та порад. Найбільш загальною метрикою є “готовність”, яка показує, наскільки ти в формі сьогодні.
Здебільшого це залежить від якості сну. І тут друга перевага Oura - найбільш детальний аналіз сну, на основі пульсу, варіабельності серцебиття, кількості рухів, часу на засинання та іншого. Навіть температуру міряє.
Також Oura дуже добре автоматично розпізнає як активності, так і сон. Краще, ніж Apple Watch. А ще в переліку активностей є “робота по дому” та “робота на дворі” - завжди на Apple Watch їх бракувало. А ще заряд тримає декілька днів.
Одним словом, якщо така дивина, як фітнес-кільце, тобі цікава, то можу запевнити — воно реально працює та для звичайної людини — не атлета — по ціннісній пропозиції навіть краще, ніж ті фітнес-браслети, які я бачив.
З недоліків: кільце Oura все ж потрібно знімати для багатьох занять, як і будь-яке кільце. А потім його легко забути. Та, також, кільце не має ніякого механізму повідомлень — що логічно, але порівняно з годинником, який може розбудити вібрацією, невигідно. Зате — ніяких відволікань. :)
12.01.2024
Обмежена паралелізація в Go
Знайшов дуже класну бібліотеку conc - вона надає чарівні компоненти для побудови одночасного коду. Наприклад, цикл з обмеженою паралельністю за один рядок:
iter.Iterator[string, []byte]{MaxGoroutines: 10}.Map(myURLs, func(url *string) []byte {
resp, _ := http.Get(*url)
return io.ReadAll(resp.Body)
})
Практично не складніше, ніж звичайний цикл. Особливо подобається використання дженериків. Хто робив таке самотужки, знає, скільки тут приховано складності. Пул горутін, канали для комунікації, група очікування.
А необмежений паралелізм — то страшна річ. Колись вже писав, чому його треба обмежувати для обчислень, але сьогодні натрапив на інший випадок. Код в циклі створював горутіни та звертався до іншого сервера. Ну поки цикл на 10 чи на 100 ітерацій, все добре. Але якщо раптом з’являється потреба в десятках тисяч запитів, то сервіс починав лягати.
Причому поки не помітив той цикл, було дуже дивно: сервіс живий, пінгується, а от чомусь в реальному використанні починає сипати тайм-аутами. Такий вийшов домашній DDoS.

