Сторонний проект Интернета вещей в моей компании начался, когда мы не смогли переустановить дверной замок, который достался от предыдущего арендатора. Это была одна из тех мелочей, о которых мы узнали после переезда в новый офис.
Обычно люди просто платят за новый замок. Но для нас это было слишком мелко, и никто не хотел дверной звонок. К тому же, мы инженеры — и хотели поиграть с каким-нибудь железом.
Цель была в том, чтобы открывать дверь телефоном или носимым гаджетом. Было несколько способов, как подойти к проблеме. В теории, мы могли использовать приложение, интеграцию в другую платформу или всё, что может отправить сигнал для открытия замка.
Chima Open Door на Pebble и iOS
К настоящему моменту в нашем дверном эксперименте мы разработали решения для интеграции со Slack, нативные приложения iOS и Android, Apple Watch и Pebble. Остановлюсь подробнее на архитектуре мобильных приложений. Признаю, что финальный продукт слегка переусложнён, но мы так его любим!
Архитектура проекта нашего IoT дверного замка
Что конкретно происходит, когда вы нажимаете кнопку в своём приложении iOS/Android? Отправляется HTTP-запрос к облачному серверу, который является сигналом для отправки сообщения к демону дверного звонка через клиентский сервер, который затем указывает релейной плате открыть замок.
Традиционно дверной замок открывается нажатием кнопки рядом с дверью. Но современные технологии позволяют выйти за пределы непосредственной физической кнопки. Вдобавок к физической кнопке, которая сигнализирует Doorlock Daemon
на диаграмме, мы добавили ещё два триггера: облачный триггер и Bluetooth Low Energy (BLE) триггер, благодаря нашему выбору железа.
В этой статье описывается облачный триггер, на который полагается ваш дверной замок.
С нажатия кнопки до записи, сохранённой на сервере Skygear
Когда пользователь нажимает кнопку открытия двери в мобильном приложении, оно обращается к облачному серверу.
Две вещи происходят на облачном сервере. Во-первых, сохраняется запись. Мы выбрали сервер Skygear Cloud Database, который позволяет синхронизировать данные с облаком. Сервер ведёт лог запросов на открытие двери.
SKYDatabase *db = [[SKYContainer defaultContainer] publicCloudDatabase];
SKYRecord *openDoor = [SKYRecord recordWithRecordType:@"OpenDoor"];
[db saveRecord:openDoor completion:^(SKYRecord *record, NSError *error) {
NSLog(@"saveOpenDoorRecordWithCompletion failed: %@", error);
if (completion) {
completion(error);
}
}];
Как только запись сохранилась, срабатывает функция after_save
из набора Skygear Cloud Functions, которая запускает в облаке наш код без необходимости развёртывания непосредственно на сервере.
Функция after_save
вызывается самим фактом сохранения записи. Асинхронно вызывается def after_open_door_save(record, original_record, db):
, когда сохраняется запись типа 'OpenDoor'
. Эта функция публикует сообщение в канале 'xxx-channel'
.
@skygear.after_save('OpenDoor', async=True)
def after_open_door_save(record, original_record, db):
publish('xxx-channel', {
'source': 'record-after-save',
'data': record.get('data', None),
})
Node Client и Clojure Server на Raspberry Pi
Следующий шаг — создать слушателя для запроса. Вот где пришло время клиента Node и сервера Clojure на Raspberry Pi. Клиент Node слушает сообщения на указанном канале сервера Skygear. Сервер Clojure — единственный, у кого есть право доступа к схеме Raspberry Pi 3. Клиент Node выдаёт запрос к серверу Clojure, как только получает сообщение.
Вот скрипт клиента Node, он содержит код, связанный с нашей конкретной конфигураций на Skygear. Указанные endPoint и API Key нужны для доступа к основному серверу на Skygear. skygear.on('xxx-channel', onReceiveOpenDoor)
означает обратный вызов функции (onReceiveOpenDoor
) при получении сообщения на канале 'xxx-channel'
.
function onReceiveOpenDoor(data) {
console.log('daemon-trigger-skygear: open door');
exec(`curl localhost:8090 --header 'X-Source: Skygear'`);
}
skygear.config({
endPoint: 'https://chimagun.skygeario.com/',
apiKey: apiKey,
}).then(() => {
skygear.loginWithUsername('xxx', 'xxx').then(() => {
skygear.on('xxx-channel', onReceiveOpenDoor);
});
});
Сервер Clojure напрямую контролирует контакты General Purpose Input/Output (GPIO) на Raspberry Pi. GPIO это штырьки на Raspberry Pi 3. Они подключены к внешней цепи, которая соединяется с дверным магнитом.
Вот код Clojure, который показывает, как Raspberry Pi открывает дверь. Когда сервер Clojure получает запрос от клиента Node, то открывает замок на три секунды. Но если в течение этих трёх секунд поступит новый запрос, то таймер переставляется ещё на три секунды. Когда время заканчивается, дверь опять запирается.
; listen on unlock-chan for unlock events
; if a new unlock event is received before the 3000ms timeout, the door is kept open.
(go-loop [unlock nil]
(when unlock
(sh "gpio" "write" "1" "1")
(loop [[trigger _] [unlock nil]]
(when trigger
(log/info (str "Unlock triggered by " (:source trigger)))
(recur (alts! [unlock-chan (timeout 3000)]))))
(sh "gpio" "write" "1" "0")
(log/info "Door Locked"))
(recur (<! unlock-chan)))
; http event listener
(run-server (fn [req]
(>!! unlock-chan {:source (or (get-in req [:headers "x-source"]) :network)})
{:status 200})
{:ip "127.0.0.1" :port 8090})
Примечание: Skygear использует американский AWS, тогда как дверь и Raspberry Pi находятся в Гонконге. Фактически, наш запрос 芝麻開門 (Chima Open Door) путешествует по всему миру, прежде чем достигнет двери.
Почему Raspberry Pi?
Вы можете спросить, почему мы выбрали именно Raspberry Pi. Мы рассматривали и платы Arduino, потому что у нас в офисе такие есть. Дело в том, что мы не могли использовать конкретную модель Arduino, поскольку хотели синхронизировать данные с Skygear JS SDK, а эта конкретная Arduino не позволяла установить сервер Node.
Кроме того, Raspberry Pi поддерживает Bluetooth Low Energy (это значит, что мы можем открывать дверь, используя третий метод, Bluetooth).
Raspberry Pi с Linux совместима с open-source бессерверной платформой Oursky
Дополнительные интеграции
Принимая во внимание, что приложение только для внутреннего использования, мы реализовали кастомную команду Slack /chima-open-door
на открытие двери, поскольку каждый сотрудник Oursky имеет доступ к Slack.
Позже другие коллеги вовлеклись в проект и написали приложение WatchOS и приложение Android, которые мы выложили на внутренней платформе. Помимо нажатия кнопки внутри приложения, мы также обеспечиваем альтернативные способы открытия двери, такие как прикосновение iOS 3D, расширение Today, виджет Android и даже интеграция с Pebble, поскольку некоторые из наших разработчиков носят такие часы.
Вот так всё сделано! Прежде чем вы погрузитесь в разработку, учтите два фактора: обратный ток (в данном случае для Raspberry Pi) и безопасность каждой из ваших интеграций. Например, мы также интегрировали доступ Bluetooth-приложения через Bluetooth Low Energy (BLE), что является самостоятельно реализуемым вариантом двухфакторной аутентификации. Среди других интеграций можете рассмотреть уведомления, когда дверь открыта (звонок, светодиод).
Если хотите узнать о любой из упомянутых технологий, не стесняйтесь выходить на контакт!
Хочу выразить благодарность моим коллегам Дэвиду Нг, Борису (akiroz), Брайану (b壹貳參肆零零) и Мэю Юнгу за работу над Android-приложением, реализацию схемы и Clojure, приложение Pebble и текст этой статьи, соответственно. Это командная работа!
Ссылки на репозитории/файлы
CloudCode: gitlab.com/oursky/doorlock-cloudcode
Клиент iOS: https://gitlab.com/oursky/doorlock-ios
Клиент Android: https://gitlab.com/oursky/doorlock-android
Клиент Pebble: https://gitlab.com/oursky/doorlock-pebble
Автор: m1rko