Перевели для вас статью Криса Хокинса, в которой он рассказывает о превращении своей квартиры в умный дом. В качестве базы используется HomePod от Apple, но, конечно, можно применять и другие системы.
У меня дома работает Apple HomePod, который помогает контролировать определенные системы в доме (к примеру, умные лампы) при помощи обычного запроса к Siri. Работает система как из дома, так и вне его (умный помощник есть на телефоне).
Skillbox рекомендует: двухлетний практический курс «Я — веб-разработчик PRO».
Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».
Изначально я скептически относился к управлению домом при помощи голосовых команд, ведь далеко не все распознается ассистентами (не только Siri) корректно. Но затем это вошло в привычку. Поскольку у ламп Hue нет физического выключателя, а в приложении нужно совершить несколько действий для управления освещением, я привлек к делу Siri.
Затем мне захотелось начать при помощи голосового помощника управлять и другими системами в доме, например, телевизором или консолью. В случае с ТВ я, например, обнаружил Simple IP Control — метод управления моим Sony Bravia путем отправки команд по TCP.
Кастомизируем Siri
Во второй половине 2018 года Apple открыла приложение Shortcuts для всех пользователей iOS. Оно позволяет автоматизировать работу с телефоном (или умным домом) без необходимости писать код.
У приложения много встроенных команд. Что ему недостает, так это возможности использовать TCP-команды, хотя там есть механизм работы с URL.
Плюс ко всему, можно писать собственные модули на Objective-C или Swift. Этого решил не делать, поскольку в будущем я могу сменить мой HomePod на другого помощника. Вместо этого мне захотелось написать веб-приложение, которое сможет отвечать на команды Siri.
Управление Sony Bravia TV
Вооружившись мануалом с командами для моего ТВ, я написал приложение на Node.js Express (Github), которое научил отвечать на некоторые из общих команд. Начал я с включения и громкости.
Команда setPowerStatus делает все, что нам нужно.
Заголовок состоит из символов * и s, которые статичны и используются для всех команд. Затем третий байт (с) используется для Command. Есть четыре значения, которые могут занимать эту позицию. C для Command (отправка команды на ТВ), E — для Enquiry (проверка текущего значения определенного параметра, например, громкости), А — Answer (отправляется в ответ на Commands и Enquiries) и N для Notify (оповещение о событиях, вроде отключение громкости).
Для достижения своей цели мне пришлось изучить документацию Sony’s JSON-RPC. Как оказалось, природа JSON-RPC over HTTP позволила упростить задачу и сократить количество кода.
Работа с JSON-RPC API была простой. Возьмем, например, сервис (system), команду (getPowerStatus) плюс параметры (true или false) и сформируем HTTP-запрос, который затем отправляем на ТВ.
let body = JSON.stringify({
method: command,
id: ++this.id,
params: params,
version: "1.0",
});
return new Promise((resolve, reject) => {
fetch('http://' + this.ip + ':' + this.port + '/sony/' + service, {
method: 'post',
headers: { 'X-Auth-PSK': this.psk },
body: body,
}).then(response => {
return response.json();
}).then(response => {
if (response.error && (!response.result || response.result.length === 0)) {
reject({ code: response.error[0] });
} else {
resolve(response.result[0]);
}
}).catch(error => {
reject(error);
});
});
По умолчанию аутентификация осуществляется при помощи предварительного ключа, отправляемого в заголовке HTTP-запроса. Но есть более безопасный и удобный способ сделать это при помощи accessControl. В этом случае мы можем отправлять команду на телевизор и осуществлять безопасный обмен кода при базовой HTTP-аутентификации. После того, как аутентификация выполнена один раз, дальнейшая авторизация выполняется при помощи cookie.
Но мой ТВ защищен файерволом, настроенным на роутере, так что я использовал предварительный ключ.
Включение Xbox One
Xbox, конечно, требовал иной настройки. Microsoft, похоже, приняла решение не использовать REST API, поэтому работа была выполнена при помощи UDP-пакетов.
К счастью, у Node.js есть модуль dgram, который «из коробки» работает со всеми возможностями USP. Вот, что у меня получилось в итоге.
turnOn() {
let socket = dgram.createSocket('udp4');
let powerPayload = new Buffer('\x00' + String.fromCharCode(this.liveId.length) + this.liveId.toUpperCase() + '\x00');
let powerHeader = Buffer.concat([new Buffer('dd0200', 'hex'), new Buffer(String.fromCharCode(powerPayload.length)), new Buffer('\x00\x00')]);
let powerPacket = Buffer.concat([powerHeader, powerPayload]);
return this._sendPacket(socket, powerPacket);
}
_sendPacket(socket, buffer) {
return new Promise((resolve, reject) => {
socket.send(buffer, 0, buffer.length, Constants.xboxPort, this.ip, function(err) {
socket.close();
if (err) {
return reject(err);
}
resolve();
});
});
}
Для настрйоки я использовал список ID-устройств, который можно найти здесь. Если вы хотите просто взять код из моего репозитория, то вам нужно заменить ID в файле config.json.
Настройка Shortcuts для Siri
Для того, чтобы Siri могла выполнять команды, которые я только что создал, ей нужен помощник. Его я создал из Raspberry Pi, поскольку «малинка» подходит по всем параметрам. Для этого я купил Pi 3 Model B+, поддерживающий Wi-Fi.
У Raspbian есть GUI для настройки. Я подключился к Wi-Fi, затем отключил дисплей и продолжил работу по SSH. Для того, чтобы убедиться в постоянной активности веб-приложения, я настроил сокет активации сервиса в systemd, так что если бы процесс Node.js упал, система автоматически могла его перезапустить.
Собственно, Shortcuts для Siri были самым простым этапом работы. Это интуитивное приложение с нативной поддержкой голосовых команд. Оно по умолчанию уже умело работать с HomePod, дополнительно настраивать ничего не понадобилось.
Собираем все вместе
Поскольку мой ТВ работает на Android, он поддерживает приложения вроде Netflix и YouTube. Помня об этом, я создал команды для запуска этих сервисов. Кроме того, я добавил команды для контроля громкости, режима работы ТВ, паузы и проигрывания контента.
Вот примеры всего, что я создал. Я также постарался сделать проект модульным, так что добавлять другие модели SmartTV — не проблема.
Вот пример модуля, который включает Xbox, ТВ и активизирует первый порт HDMI.
router.post('/turnOnXboxAndTV', function(req, res, next) {
Promise.all([
xbox.turnOn(),
tv.turnOn()
.then(() => new Promise(resolve => setTimeout(resolve, 2000)))
.then(() => tv.setInput(config.scripts.xboxInput)),
]).then(() => {
res.sendStatus(200);
}).catch((error) => {
res.status(500).send(error);
});
});
А вот как это все работает на практике.
К сожалению, функциональность Siri не слишком хороша. У той же Alexa от Amazon гораздо более обширный спектр возможностей и весьма мощный API. Думаю, на основе Alexa можно создать куда более серьезные проекты.
Skillbox рекомендует:
- Практический курс «Мобильный разработчик PRO».
- Онлайн-курс «Профессия frontend-разработчик».
- Практический годовой курс «PHP-разработчик с 0 до PRO».