Канал
У Льоні сьогодні
Нотатки архітектора систем та майстра на всі руки. Що я зробив, що з того вийшло, і що це все значить.
- ActiveRecord
- AmazonRedshift
- API
- AppleScript
- AWS
- AWSLambda
- Cadencia
- CGO
- Chezmoi
- CI
- Clojure
- Cloudflare
- CloudflarePages
- CloudWatch
- CssParser
- CтохастичнийТаймтрекер
- DNS
- Docker
- Dotfiles
- Fly.io
- GCP
- Git
- GitHub
- GitHubActions
- Go
- Golang
- GTD
- HomeAssistant
- Hugo
- I18next
- JavaScript
- Jira
- JSON
- Kafka
- MacMiniВДорозі
- MacOS
- Markdown
- Mastodon
- Obsidian
- ObsidianCanvas
- OmniWope
- OpenSearch
- Oura
- PagerDuty
- Ping
- Plausible
- PostgreSQL
- ReactNative
- Redis
- RSS
- Ruby
- RubyOnRails
- Sentry
- Sintra
- Slack
- SMTP
- SQL
- SQLite
- Svelte
- Swift
- SwiftData
- SwiftUI
- Telegram
- Terraform
- TLS
- TypeScript
- Vercel
- VPN
- WeightPlot
- WordPress
- XCode
- Адвент2024
- Бази Даних
- БДС
- Безпека
- Блокнот
- Вебтехнології
- ВолюІнформації
- Гаджети
- ДизайнМовПрограмування
- ДомашняПанель
- Ігри
- Інструменти
- ІнтеграційніТести
- Кава
- КеруваннняЗадачами
- Кодогенерація
- Криптографія
- Локалізація
- Маркетинг
- МетаПост
- МоїПроєкти
- Навігація
- Оптимізація
- ОсновиІнтернетБезпеки
- Помічник ШІ
- ПомічникШІ
- ПостПроПохід
- Програмування
- Продуктивність
- Проєкти
- Проза
- РДУГ
- Рівночасність
- РобочийКомп
- Розробка
- РозумнийБудинок
- СинПопросивПриготувати
- Сон
- СтохастичнийТаймтрекер
- Танці
- ХмарніТехнології
-
Тиск в шинах через OBD
Спочатку, історія. В мене з машиною два рази траплялася ситуація однакова до дрібниць.
Виїжджаю я на трасу в холодний дощовий день. Спрацьовує датчик тиску. Я думаю — це, певно, через низьку температуру, а не пробій. Перевіряти тиск манометром під дощем не хочеться… Тож їду далі.
…Через 100-200 кілометрів траси вибухає шина. Так, таке буває, якщо їздити на пробитому колесі! Просто вся бокова поверхня рветься і до побачення. Звісно, така шина не ремонтується, тож наслідки довгі — міняти на докатку, їхати 70 км/год до місця призначення, потім чи шукати пару таких саме шин, чи всі чотири…
Останній раз таке трапилося в перший день довжелезної подорожі. А шини були практично нові. І мені дуже пощастило знайти магазин, який погодився продати одну шину, та ще й у вихідні на свята. Тепер щиро рекомендую avtopik.ua.
(Так, в мене в машині є і докатка, і манометр, і помпа. Бракує тільки здорового глузду інколи.)
Отже. Один раз то випадковість, але другий — вже система… Зрозумів, що мені з такою хибою мислення треба чітко знати тиск в шинах, щоб наступного разу не вгадувати. На жаль, в моїй CX-5 є тільки один індикатор “низький тиск”. Тож може треба альтернативний комплект?
На щастя, шукати датчики я став з Клодом, а Клод звернув увагу, що на мою машину є читачі для OEM датчиків. Зручно! Втім, поки шукали такий читач, виявили що можна ще зручніше. Хоч видимий індикатор і найпростіший, але всі чотири значення тиску доступні на бортовій шині!
Лишилося прочитати значення. Для того придбав адаптер OBDLink MX+ та струсив пил з застосунку OBD Fusion, який в мене був ще з минулої машини. Пʼятнадцять хвилин налаштувань — та в мене є красивий екран з усіма чотирма показниками.
Я був приємно здивований тим, що вдалося знайти розвʼязок без радикальних змін та ще й такий, про який я сам й гадки не мав.
-
Індикація відключень світла на домашній панелі
В продовження теми про домашню панель: як я використав інтеграцію Yasno Outages від пана Дениса Довганя для спрощення життя.
Інформація щодо наявності світла приходить з двох джерел. Для де-факто є датчик потоку від Shelly, який скаже, що на вході з мережі точно є напруга. А для де-юре - Yasno Outages; ця інтеграція повідомляє час поточного відключення та заживлення. Всі дані приходять до мене з Home Assistant.
Далі залишається подружити їх в такій формі, щоб було зрозуміло користувачу.
Індикатор “світло є/нема” береться, звісно, з наявності споживання з мережі. Там довелося лише ігнорувати маленькі значення потоку, бо час від часу датчик знімав якісь 5W навіть коли мережа була мертвою. (Може, хтось може мені це пояснити?) На практиці навіть в холостому режимі квартира споживає більше.
Розклад береться з Yasno. Тут все прозоро.
Складність прихована в рядку з “ще”, тобто у відліку часу, що лишився. Бо знаємо, що графік та реальність часто розбігаються. Тому час “ще” обирається відповідно до наявного стану: якщо світло є, то до наступного запланованого відключення; нема — до наступного заживлення. Інколи то дає дивні значення (як-от якщо світло є, а мали б відключити, то покаже “ще 10 год” - тобто проскочить заплановане відключення взагалі.)
Можна ще багато це покращувати, але не хотілося б.
-
В батареї нема відсотка заряду
Взимку я писав про свою домашню панель - проєкт, який дійсно спростив життя сімʼї!
Зокрема, на панелі можна побачити стан заряду домашньої батареї. Показати його складніше, ніж здається.
В батареї немає “відсотку заряду”, якщо це не батарея з компʼютерної гри.
(Принаймні в хімічної батареї. Бо, наприклад у гравітаційної батареї відсоток є! Але я відвернувся.)
Отже. Якщо на побутовому рівні, то в хімічній батареї заряд накопичується зміною хімічного складу тої батареї, а вимірюється він — і тут ви можете мене гарно виправити — напругою. Тобто по напрузі на батареї система може зробити здогадку про те, який в ній лишився заряд.
Але і це ще не все. Бо хімічні батареї втрачають місткість — зношуються, так би мовити. Як дізнатися, наскільки? В контролера батареї є певні здогадки, виходячи з того, наскільки (до якої напруги) батарею можна зарядити.
Контролер батареї повідомляє нам два числа: поточний заряд в ампер-годинах, та максимальний заряд в ампер-годинах. Здавалося б, ну хоч тут починається арифметика: ділимо поточний на максимальний.
Проте ні! На практиці виходить, що максимальний заряд — це не факт, а статистична оцінка. Та поточний заряд цілком нормально може бути більше за максимальний. Або навпаки — його не досягати, скільки не заряджай батарею.
Як тоді показати, що батарея заряджена?
Брехати.Оцінювати наближено.if (charge > 0.99*charge_max) charge_percent = 100Так і недозаряд, і перезаряд будуть приховані. Причому така проблема не тільки для 100% зʼявиться. От, наприклад, айфони мають режим зарядки до 80%. Думаєте, там система математично чесні 80% обчислює? Я впевнений, що ні!
-
Визначення BPM силами користувача — яке працює
Отже… як же ж його допомогти користувачу визначити темп пісні?
Я залишив модель “теп на раз”, але з ключовою різницею.
Перша ітерація бачила послідовність тепів як сигнал, який ми перетворюємо на BPM. Більше тепів — краще сигнал… це якщо тепи чіткі. Але більше тепів — більше шансів на помилку. Особливо тому, що пісня — це не бій метронома, та не кожний “раз” ти почуєш однаково.
Оскільки характер пісні змінюється, фактично в нас є шанс задати чітку послідовність, тільки поки грає куплет чи приспів, чи інша рівномірна частина. Розумієш — в моделі “послідовність тепів” є практична стеля якості.
Тому я знайшов іншу модель. Пісня містить послідовність “разів”, та наша задача — її викрити. Тепати можна в будь-який “раз” - не обовʼязково в кожний. Всі “рази”, який відмітить користувач, додають інформації: між двома “разами” завжди є кратний проміжок.
Тоді користувач вільний обрати ті “рази”, де їх добре чути. І від того сигнал стає чистішим!
Ба більше, виходить, що формула впевненості значно виграє, якщо в нас є тепи з початку та з кінця пісні. Бо та ж похибка на великому проміжку значить менше! А оце вже зовсім інший досвід!
Бо раніше було навпаки: навіть маленька похибка в тепах могла протягом цілої пісні повністю зсунути ритм. Тоді доводилося старатися та бути чіткіше. І це я розумів, а як пояснити простому користувачеві?
Можливо, я ще перероблю цей UI, щоб більше спрямовувати користувача до правильних дій — як-от визначення “раз” на початку та кінці пісні. Але в цілому — вже задоволений.
-
Маніфест Холсті — тепер українською
У червні в пошуках підходів до організації життя мені потрапив на очі маніфест Холсті. По-перше, це красиво — плакат з типографічною заливкою виглядає ефектно. А по-друге, зміст мені відгукнувся та сподобався.
От взяв би та й повісив той плакат. Є тільки одна проблема… Хотілося бачити його українською.
Тож як дійшли руки, взяв та й переклав. Переклад вільний, тобто я не ставив задачу ідеально відтворити слова, а натомість підібрати влучні та поетичні слова. До прикладу: фразу “share your passion” в кінці я переклав як “запалюй інших”.
Звісно що переклад не автоматичний, бо який в цьому взагалі інтерес. Для людей я пишу від себе.
А от в чому LLM допомогла суттєво, так це у викладці тексту у вигляді заливки. Причому майже всю ідею вхопила з першого ж запиту — залишилося чимало часу витратити на дрібні виправлення.
Також LLM підготувала версію для друку та відразу конвертувала її в PDF. Та зробила повноцінний сайт з усією атрибутикою — як-от метадані для поширення. Навіть картинку для поширення, на якій зображений сам маніфест! Такі рутинні задачі стають тривіальними, і мені то дуже подобається.
Тримай посилання: Маніфест Holstee - українською.
-
Визначення BPM силами користувача
В моїй програмі для музичності](https://cadencia.dance) багато завʼязано на темп пісні, тобто на її BPM. Практика показує, що автоматичні засоби визначення BPM помиляються. Окрім того, мені також треба знати, де “раз”, тобто де починається вісімка (бо, взагалі-то, як мінімум бачата та сальса для танцівників складаються з вісімок.)
Отже, розповідь про дві ітерації інтерфейсу для цього… з їхніми недоліками.
На початку я зробив варіант “тепай на 1”. Тобто ти задаєш послідовність “разів”, та з неї програма обчислює частоту тактів. Помилився — починай наново.
(Що робити в разі помилки — це одне з головних питань. Причому як до дизайнера, так і до користувача.)
Проблемою цього варіанту було те, що точок даних було замало. А значить, дрібні помилки в тепах сильно впливали на результат. Тож тепати доводилося довго… що дедалі збільшувало шанс помилки, бо не завжди легко той “раз” почути — дійдеш до якогось брейкдауну і сиди вгадуй.
Тоді я спробував зробити більше точок: тепати на кожний рахунок, а не тільки на “раз”. Будь-який танцівник вже вміє крокувати на кожний рахунок, тож, в теорії, тепати теж вийде?
Як би не було, а “тепати в рахунок” на досвіді виявилося незручніше. Впевненість субʼєктивно просіла помітно. Шанс на помилки теж зріс: бо тепер тепати треба не раз на 4 секунди (приблизно), а 2 рази на секунду.
Здається, тепер я зрозумів, у чому річ: чіткий рахунок не так вже й легко почути. Коли танцюєш, мати варіативність кроку не тільки дозволено, але й може бути музично та гарно. Підтримувати стабільний ритм — задача більше для барабанника, ніж танцівника.
Отже… що робити? Як отримати BPM від користувача — в ідеалі так, щоб навіть не називати слово BPM?
Успішною стала третя ітерація, але про неї завтра.
-
Захист від копіювання — це завжди лише ускладнення
Оскільки відтворення цифрового змісту будь-якого роду є, неодмінно, його копіюванням — технічно неможливо заборонити копіювання категорично.
До прикладу. У бабці був канал в популярному соціальному месенджері. Закритий. З обмеженням копіювання! На жаль бабця померла та не лишила після себе ключів. В нас стоїть задача зберегти бабцини світлини та відео поза межами платформи.
Можна шукати грубі шляхи — перезняти з екрана камерою? Відкрити у вебверсії, яка не може захистити від знімків екрана?
А можна замість того згадати, що в цього месенджера є не тільки Bot API, а й відкритий Client API. (Що, credit where credit is due, гідний та важливий крок.) З використанням цього Client API можна побудувати повноцінний клієнт месенджера, який вмітиме авторизуватися вже у твій обліковий запис, не бота — та матиме доступ до всього, що й звичайний клієнт. Буквально, до всіх функцій.
Ну а коли далі той клієнт буде не відтворювати відео, а зберігати на диск — то це вже його справа. Бо зміст в нього вже є.
Серйозні пани з кіностудій вже попіклувалися про це. Тому ми маємо HDCP та інші інновації, які обмежують, хто саме має доступ до змісту. (Ну й хіба воно працює?)
Ба більше, якщо раніше написати власний клієнт було справою зухвалих — а решті доводилося шукати екземпляри на сумнівних форумах — то зараз LLM тобі його згенерує за пʼять хвилин. Ще й під конкретну потребу: може каталог каналу згенерувати, чи перейменувати як треба тощо.
Величезний дисклеймер: тільки не розповсюджуйте закритий контент. Серйозно.
-
Пости про походи вертаються — завдяки LLM
В далекому 2023 році я витратив чимало часу, щоб зробити красивий пост про похід на Клаферкесель з анімованою картою та журнальним форматом. Витратив… і зрозумів: на другий такий в мене нескоро буде час. Зокрема підгодування карти та треків було рутинною роботою, та ще й вимагала серйозної математики. У 2025 я спробував щось відтворити, напоровся на баги з проєкціями та знову відклав.
Та ось… Цього разу взявся за справу із Курсором. І воно пішло! LLM легко порається з задачею “зроби мені карту по аналогії, але іншого місця” - тут я був впевнений.
А що здивувало, так це те що Курсор достатньо візуально обладнаний, щоб вручну налагоджувати проблеми з вирівнюванням текстур та розташуванням обʼєктів на карті. А проблеми були суттєві. От він брав, рендерив кадри з різними версіями, та перевіряв. Наприклад, наклав координати міст України на текстури Європи — та помітив, що міста не на місці. Серйозно вражає. Я гадав, що ми будемо робити це в парі — моїми очима — а потреби не було.
Та друга ручна задача: підчистити GPS-треки прогулянок для мапи. Та хоча б підрізати. Тож я скористався трюком: згенеруй мені редактор для того, щоб обрізати ці треки по довжині. І він згенерував! (Дивись ілюстрацію.) Та я за пʼять хвилин встиг закінчити цю роботу, жодного разу не дивившись в файл з координатами.
Або ще: локальна 3D-мапа в мене була квадратна, бо на це вистачило зусиль. Але кругла ж красивіше! Тож Курсор за один запит переробив квадратну на круглу (відредагував 3D-сітку, на яку накладаються текстури.)
Або ще краще: мені хотілося, щоб під час анімації на карті “їхав потяг”. Хтозна скільки б я це робив вручну, а тут встиг за годинку паралельно з іншими задачами. (А в старому пості тепер їде машинка з Відня, бо чому б й ні.)
А далі — вже робота над матеріалом: фотографії впорядкувати, тексти написати. Звісно, в цьому мені допомога не потрібна. Ну хіба що LLM швидше та точніше заміняє таку ручну роботу як “візьми ці пʼять фотографій та винеси в окрему галерею”.
Тож багато надії, що справа буде рухатися!
Результат ось: Що ми робили в Яремче. Краще дивитися на великому екрані, хоча для телефонів, я гадаю, ми з Курсором теж щось придумаємо, якщо піде. (Поки що я розробляв дизайн для моніторів, а на телефон лише підганяв, щоб мало хоч якийсь приємний вигляд.)
-
_id в OpenSearch - не так ефективно, як можна подумати
В продовження теми того, що розуміння однієї бази даних не завжди переноситься на іншу.
В OpenSearch (чи ElasticSearch, бо одно й те саме) в кожного документа є ідентифікатор -
_id. Наче зрозуміле явище, за ним ми швидко знаходимо та змінюємо документи. Так саме як в PostgreSQL, наприклад.Але. Якщо вам раптом захочеться відсортувати за
_id, то виявиться, що прямої можливості це зробити в OpenSearch немає. Словник того не підтримує. Та тому для виконання запиту з"sort": "_id"база завантажить всі ID в памʼять. Цей маневр називається field data.З мільйонами документів в індексі ця
fielddataзайматиме гігабайт RAM, а то й більше — саме так я про неї й дізнався. Та дуже дивно було, що саме_id, з усіх полів, спричиняє такі витрати.Що робити натомість? Можна дублювати
_idвсередині документу. Можна сортувати за якимсь іншим полем. Зокрема, якщо задачею сортування є стабільність видачі (бо навіщо ще сортувати за ID?) - то в пошуку є спеціальне поле_shard_docяк раз для такої потреби.Власне, в останніх версіях OpenSearch навіть є налаштування
indices.id_field_data.enabled: false- воно примусово заборонить створенняfielddataз_id. Тож то й таке. -
Вірний вибір каналу для сповіщень
Уявимо собі що ми робимо сповіщення про вибіг терміну TLS сертифікату. (А знаєте, краще не уявляти, а таке сповіщення мати точно — якщо так сталося що сертифікат не оновлюється автоматично.)
Ми розробники, звикли працювати з кодом та кидати помилки в Sentry. Тож робимо цикл-монітор, який перевіряє стан сертифіката та, якщо він наближається терміну, надсилаємо відповідну помилку.
Перші рази все працює. Минають місяці… як от прокидаємося вночі від тривоги на брак сертифіката. Йой, не подивилися вчасно в Sentry. А річ у тім, що Sentry це взагалі не місце для тривожних сповіщень! Так, певні помилки ми там побачимо. Але зазвичай в Sentry дивляться вже тоді, коли іншим чином виявили проблему — хтось поскаржився, наприклад. Тому як ти не фразуй сповіщення, а ставлення до нього буде недостатньо відповідальним.
Добре, тоді може направити повідомлення в канал Slack? Це краще для помітності, навіть надто гарно. Бо монітор, щоб бути помітним, почне бомбити канал постійно. Він же ж не знає, що ми побачили. А нам взагалі-то треба побачити одне сповіщення, відреагувати та зробити сертифікат — на що піде не один день. Ну таке.
(Робити власну складнішу систему, звісно, теж не хочеться.)
Можна зайти з іншого боку — дата вибігу сертифіката відома. Тож можна створити тікет в Jira з терміном. Це просто, але покладається на те, що хтось буде відповідальним та побачить той тікет. Що — для задачі, яка відбувається раз на три місяці та потребує неухильного виконання — мені не подобається. Потрібний механізм, що буде смикати за руку, і робити це надійно.
На цих думках я згадую про PagerDuty. Оце воно! А щоб сповіщення зʼявилося там, сервіс надсилає в CloudWatch метрику “залишок терміну дії сертифіката”.
І як тільки я бачу задачу у вигляді метрики, картинка складається. Бо тоді є чітка історія стану. Є вбудована у Cloudwatch обробка випадку “монітор зламався та нічого не надсилає”. Є готовий механізм сповіщень, та впевненість, що хтось побачить та підтвердить попередження. А головне, що це лягає в систему інших критичних сповіщень, тобто стає зрозумілим для команди.
(Нарешті, маленька порада про PagerDuty: в нас в описі кожної тривоги є посилання на посібник по її виправленню. Чудово допомагає, коли тривога будить тебе о третій ранку.)