Pull to refresh

Как переусложнить дверной замок

Reading time5 min
Views18K
Original author: Steven Chan


Сторонний проект Интернета вещей в моей компании начался, когда мы не смогли переустановить дверной замок, который достался от предыдущего арендатора. Это была одна из тех мелочей, о которых мы узнали после переезда в новый офис.

Обычно люди просто платят за новый замок. Но для нас это было слишком мелко, и никто не хотел дверной звонок. К тому же, мы инженеры — и хотели поиграть с каким-нибудь железом.

Цель была в том, чтобы открывать дверь телефоном или носимым гаджетом. Было несколько способов, как подойти к проблеме. В теории, мы могли использовать приложение, интеграцию в другую платформу или всё, что может отправить сигнал для открытия замка.


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
Клиент iOS
Клиент Android
Клиент Pebble
Tags:
Hubs:
Total votes 20: ↑18 and ↓2+16
Comments22

Articles