В этом документе описывается, как использовать API-интерфейсы Bluetooth , Bluetooth Socket и Bluetooth Low Energy для связи с устройствами Bluetooth и Bluetooth Low Energy.
Дополнительную информацию о Bluetooth см. в официальных спецификациях Bluetooth .
Требования к манифесту
Для приложений Chrome, использующих Bluetooth, добавьте запись Bluetooth в манифест и укажите, если необходимо, UUID профилей, протоколов или служб, которые вы хотите реализовать, а также хотите ли вы реализовать их с помощью API-интерфейсов сокета и/или Low Energy.
Например, для реализации сокета:
"bluetooth": {
"uuids": [ "1105", "1106" ],
"socket": true
}
И для реализации с низким энергопотреблением:
"bluetooth": {
"uuids": [ "180D", "1809", "180F" ],
"low_energy": true
}
Чтобы получить доступ только к состоянию адаптера, обнаружить близлежащие устройства и получить базовую информацию об устройствах, требуется только сама запись:
"bluetooth": {}
Информация об адаптере
Получение состояния адаптера
Чтобы получить состояние адаптера Bluetooth, используйте метод bluetooth.getAdapterState :
chrome.bluetooth.getAdapterState(function(adapter) {
console.log("Adapter " + adapter.address + ": " + adapter.name);
});
Уведомления адаптера
Событие bluetooth.onAdapterStateChanged отправляется при каждом изменении состояния адаптера. Это можно использовать, например, для определения того, включен или выключен радиоадаптер.
var powered = false;
chrome.bluetooth.getAdapterState(function(adapter) {
powered = adapter.powered;
});
chrome.bluetooth.onAdapterStateChanged.addListener(
function(adapter) {
if (adapter.powered != powered) {
powered = adapter.powered;
if (powered) {
console.log("Adapter radio is on");
} else {
console.log("Adapter radio is off");
}
}
});
Информация об устройстве
Список известных устройств
Чтобы получить список устройств, известных адаптеру Bluetooth, используйте метод bluetooth.getDevices :
chrome.bluetooth.getDevices(function(devices) {
for (var i = 0; i < devices.length; i++) {
console.log(devices[i].address);
}
});
Возвращаются все устройства, включая сопряженные устройства и недавно обнаруженные устройства. Обнаружение новых устройств не начнется (см. Обнаружение ближайших устройств ).
Получение уведомлений устройства
Вместо многократного вызова bluetooth.getDevices вы можете использовать события bluetooth.onDeviceAdded , bluetooth.onDeviceChanged и bluetooth.onDeviceRemoved для получения уведомлений.
Событие bluetooth.onDeviceAdded отправляется всякий раз, когда устройство обнаруживается адаптером или устанавливает соединение с адаптером:
chrome.bluetooth.onDeviceAdded.addListener(function(device) {
console.log(device.address);
});
Добавление прослушивателя для этого события не запускает обнаружение устройств (см. Обнаружение ближайших устройств ).
Изменения в устройствах, включая подключение ранее обнаруженных устройств, уведомляются событием bluetooth.onDeviceChanged :
chrome.bluetooth.onDeviceChanged.addListener(function(device) {
console.log(device.address);
});
Наконец, событие bluetooth.onDeviceRemoved отправляется всякий раз, когда сопряженное устройство удаляется из системы или когда обнаруженное устройство в последнее время не было обнаружено:
chrome.bluetooth.onDeviceRemoved.addListener(function(device) {
console.log(device.address);
});
Обнаружение ближайших устройств
Чтобы начать обнаружение ближайших устройств, используйте метод bluetooth.startDiscovery . Обнаружение может быть ресурсоемким, поэтому по завершении вам следует вызвать bluetooth.stopDiscovery .
Вы должны вызывать bluetooth.startDiscovery всякий раз, когда вашему приложению необходимо обнаружить близлежащие устройства. Не делайте вызов обусловленным свойством discovering
bluetooth.AdapterState . Вызов будет успешным, даже если другое приложение обнаружит близлежащие устройства, и обеспечит продолжение обнаружения адаптером после остановки этого другого приложения.
Информация о каждом вновь обнаруженном устройстве поступает с помощью события bluetooth.onDeviceAdded . Для устройств, которые уже были обнаружены недавно или ранее были сопряжены или подключены, событие не будет отправлено. Вместо этого вам следует вызвать bluetooth.getDevices для получения текущей информации и использовать событие bluetooth.onDeviceChanged для получения уведомлений об изменениях этой информации в результате обнаружения.
Пример:
var device_names = {};
var updateDeviceName = function(device) {
device_names[device.address] = device.name;
};
var removeDeviceName = function(device) {
delete device_names[device.address];
}
// Add listeners to receive newly found devices and updates
// to the previously known devices.
chrome.bluetooth.onDeviceAdded.addListener(updateDeviceName);
chrome.bluetooth.onDeviceChanged.addListener(updateDeviceName);
chrome.bluetooth.onDeviceRemoved.addListener(removeDeviceName);
// With the listeners in place, get the list of devices found in
// previous discovery sessions, or any currently active ones,
// along with paired devices.
chrome.bluetooth.getDevices(function(devices) {
for (var i = 0; i < devices.length; i++) {
updateDeviceName(devices[i]);
}
});
// Now begin the discovery process.
chrome.bluetooth.startDiscovery(function() {
// Stop discovery after 30 seconds.
setTimeout(function() {
chrome.bluetooth.stopDiscovery(function() {});
}, 30000);
});
Если пользователь выключит радиомодуль Bluetooth, все сеансы обнаружения будут завершены и не возобновятся автоматически при включении радиомодуля. Если это важно для вашего приложения, вам следует следить за событием bluetooth.onAdapterStateChanged . Если свойство discovering
изменится на false
, вашему приложению потребуется снова вызвать bluetooth.startDiscovery для возобновления работы. Будьте осторожны с ресурсоемким характером открытий.
Идентификация устройств
Для идентификации устройств, возвращаемых bluetooth.getDevices , и связанных с ними событий предоставляется ряд различных опций.
Если устройство поддерживает спецификацию идентификатора устройства Bluetooth, к объекту «Устройство» добавляется несколько свойств, содержащих поля, определенные этой спецификацией. Пример:
chrome.bluetooth.getDevices(function(devices) {
for (var i = 0; i < devices.length; i++) {
if (devices[0].vendorIdSource != undefined) {
console.log(devices[0].address + ' = ' +
devices[0].vendorIdSource + ':' +
devices[0].vendorId.toString(16) + ':' +
devices[0].productId.toString(16) + ':' +
devices[0].deviceId.toString(16));
}
}
});
Спецификации идентификатора устройства обычно достаточно, чтобы идентифицировать конкретную модель и даже версию устройства от поставщика. Если его нет, вместо этого вам придется полагаться на информацию о классе или типе устройства, которая может быть объединена с префиксом производителя в address
.
Большинство устройств Bluetooth предоставляют информацию о классе устройства в виде битового поля, интерпретируемого в соответствии с документом «Назначенные номера базовой полосы» . Это битовое поле доступно в свойстве deviceClass
.
chrome.bluetooth.getDevices(function(devices) {
for (var i = 0; i < devices.length; i++) {
if (devices[0].vendorIdSource != undefined) {
console.log(devices[0].address + ' = ' +
devices[0].deviceClass.toString(16));
}
}
});
Анализ поля может быть сложным, поэтому для наиболее распространенных типов устройств Chrome делает это за вас и устанавливает поле type
. Если это недоступно или недостаточно для ваших нужд, вам придется самостоятельно проанализировать deviceClass
.
chrome.bluetooth.getDevices(function(devices) {
for (var i = 0; i < devices.length; i++) {
if (devices[0].vendorIdSource != undefined) {
console.log(devices[0].address + ' = ' + devices[0].type);
}
}
});
Использование RFCOMM и L2CAP
Приложения Chrome могут подключаться к любому устройству, поддерживающему службы RFCOMM или L2CAP. Сюда входит большинство классических Bluetooth-устройств, представленных на рынке.
Подключение к розетке
Чтобы подключиться к устройству, вам нужны три вещи. Сокет для подключения, созданный с помощью bluetoothSocket.create ; адрес устройства, к которому вы хотите подключиться, и UUID самой службы.
Прежде чем устанавливать соединение, вы должны убедиться, что адаптер знает об устройстве, используя bluetooth.getDevice или API обнаружения устройств.
Информация, необходимая для установления базового соединения, в том числе о том, следует ли использовать протокол RFCOMM или L2CAP и какой канал или PSM, получается с помощью обнаружения SDP на устройстве.
Пример:
var uuid = '1105';
var onConnectedCallback = function() {
if (chrome.runtime.lastError) {
console.log("Connection failed: " + chrome.runtime.lastError.message);
} else {
// Profile implementation here.
}
};
chrome.bluetoothSocket.create(function(createInfo) {
chrome.bluetoothSocket.connect(createInfo.socketId,
device.address, uuid, onConnectedCallback);
});
Сохраните дескриптор идентификатора сокета, чтобы позже можно было отправлять данные ( bluetoothSocket.send ) в этот сокет.
Получение и отправка в сокет
Для получения данных из сокета и отправки в него используются объекты ArrayBuffer
. Чтобы узнать о ArrayBuffers, ознакомьтесь с обзором « Типизированные массивы JavaScript» и руководством « Как преобразовать ArrayBuffer в String и обратно» .
Чтобы отправить данные, которые есть в arrayBuffer
используйте bluetoothSocket.send :
chrome.bluetoothSocket.send(socketId, arrayBuffer, function(bytes_sent) {
if (chrome.runtime.lastError) {
console.log("Send failed: " + chrome.runtime.lastError.message);
} else {
console.log("Sent " + bytes_sent + " bytes")
}
})
В отличие от метода отправки данных, данные принимаются в событии ( bluetoothSocket.onReceive . Сокеты создаются без паузы (см. bluetoothSocket.setPaused ), поэтому прослушиватель этого события обычно добавляется между bluetoothSocket.create и bluetoothSocket.connect .
chrome.bluetoothSocket.onRecieve.addListener(function(receiveInfo) {
if (receiveInfo.socketId != socketId)
return;
// receiveInfo.data is an ArrayBuffer.
});
Получение ошибок сокета и отключения
Чтобы получать уведомления об ошибках сокета, включая отключение, добавьте прослушиватель к событию bluetoothSocket.onReceiveError .
chrome.bluetoothSocket.onReceiveError.addListener(function(errorInfo) {
// Cause is in errorInfo.error.
console.log(errorInfo.errorMessage);
});
Отключение от розетки
Чтобы прервать соединение и отключить сокет, используйте bluetoothSocket.disconnect .
chrome.bluetoothSocket.disconnect(socketId);
Издательские услуги
Помимо исходящих подключений к устройствам, приложения Chrome могут публиковать сервисы, которые могут использоваться любым устройством, поддерживающим RFCOMM или L2CAP.
Прослушивание сокета
Поддерживаются два типа опубликованных сервисов. RFCOMM используется наиболее часто и охватывает большинство устройств и профилей:
var uuid = '1105';
chrome.bluetoothSocket.create(function(createInfo) {
chrome.bluetoothSocket.listenUsingRfcomm(createInfo.socketId,
uuid, onListenCallback);
});
L2CAP — второй вариант, который охватывает другие типы устройств и использование в зависимости от поставщика, например, загрузку встроенного ПО.
var uuid = '0b87367c-f188-47cd-bc20-a5f4f70973c6';
chrome.bluetoothSocket.create(function(createInfo) {
chrome.bluetoothSocket.listenUsingL2cap(createInfo.socketId,
uuid, onListenCallback);
});
В обоих случаях может быть передан необязательный bluetoothSocket.ListenOptions для выделения определенного канала или PSM. Обратный вызов указывает на ошибку через chrome.runtime.lastError
и успех в противном случае. Сохраните дескриптор идентификатора сокета, чтобы позже можно было принимать соединения ( bluetoothSocket.onAccept ) из этого сокета.
Прием клиентских подключений
Клиентские соединения принимаются и передаются в ваше приложение через событие bluetoothSocket.onAccept .
chrome.bluetoothSocket.onAccept.addListener(function(acceptInfo) {
if (info.socketId != serverSocketId)
return;
// Say hello...
chrome.bluetoothSocket.send(acceptInfo.clientSocketId,
data, onSendCallback);
// Accepted sockets are initially paused,
// set the onReceive listener first.
chrome.bluetoothSocket.onReceive.addListener(onReceive);
chrome.bluetoothSocket.setPaused(false);
});
Прекратить принимать клиентские подключения
Чтобы прекратить прием клиентских подключений и отменить публикацию службы, используйте bluetoothSocket.disconnect .
chrome.bluetoothSocket.disconnect(serverSocketId);
Взаимодействие с устройствами с низким энергопотреблением
Bluetooth Low Energy или (Bluetooth Smart) — беспроводная технология, направленная на снижение энергопотребления. API Bluetooth Low Energy позволяет приложениям выполнять центральную роль в подключении LE к периферийному устройству. В следующих разделах описывается, как обнаруживать, подключаться и взаимодействовать с периферийными устройствами Bluetooth Low Energy.
Обнаружение и подключение к периферийным устройствам
Как и традиционные устройства Bluetooth, периферийные устройства LE можно обнаружить с помощью методов, описанных в разделе «Обнаружение близлежащих устройств» . Устройство LE становится доступным для обнаружения путем отправки пакетов данных, называемых «Рекламные данные», и считается, что устройство находится в рекламном режиме . Рекламные данные могут содержать UUID сервисов, доступных на устройстве. Если они присутствуют, эти UUID будут доступны с использованием свойства uuids
соответствующего объекта bluetooth.Device .
После обнаружения устройство LE можно подключить, вызвав bluetoothLowEnergy.connect , чтобы приложение могло взаимодействовать со своими службами:
chrome.bluetooth.onDeviceAdded.addListener(function(device) {
var uuid = '0000180d-0000-1000-8000-00805f9b34fb';
if (!device.uuids || device.uuids.indexOf(uuid) < 0)
return;
// The device has a service with the desired UUID.
chrome.bluetoothLowEnergy.connect(device.address, function () {
if (chrome.runtime.lastError) {
console.log('Failed to connect: ' + chrome.runtime.lastError.message);
return;
}
// Connected! Do stuff...
...
});
});
После подключения свойство connected
соответствующего объекта bluetooth.Device будет иметь значение true
. Вызов bluetoothLowEnergy.connect устанавливает требование приложения о физическом соединении с устройством. Физическое соединение с устройством может существовать без вызова bluetoothLowEnergy.connect (например, из-за другого приложения). В этом случае, хотя ваше приложение по-прежнему может взаимодействовать со службами устройства, оно всегда должно вызывать bluetoothLowEnergy.connect , чтобы другое приложение не могло отключить физическую связь.
Как только вашему приложению больше не требуется подключение, оно может удалить свое требование о соединении, вызвав bluetoothLowEnergy.disconnect :
chrome.bluetoothLowEnergy.disconnect(deviceAddress);
Обратите внимание, что это не обязательно приведет к разрушению физической связи с устройством, поскольку могут быть другие приложения, которые имеют активное подключение к устройству. Иногда устройство может отключиться по причинам, не зависящим от приложения (например, если устройство пропадает или отключается пользователем явно через утилиты операционной системы). Ваше приложение должно отслеживать событие bluetooth.onDeviceChanged, чтобы получать уведомления об изменениях в соединении и при необходимости повторно подключаться.
После подключения устройство, на котором работает Chrome, будет играть так называемую центральную роль , а удаленное устройство — периферийную роль . На этом этапе ваше приложение может взаимодействовать со службами на устройстве, используя методы, описанные в следующем разделе. Примечание. API в настоящее время не поддерживают работу в качестве периферийного устройства LE; приложения могут реализовывать только центральную роль.
Сервисы, характеристики и дескрипторы
Bluetooth Low Energy основан на простом протоколе запроса-ответа, называемом протоколом атрибутов (ATT). Используя ATT, центральное устройство взаимодействует с так называемыми атрибутами на периферийном устройстве, соответствуя специальному профилю Bluetooth, называемому общим профилем атрибутов (GATT). ГАТТ определяет следующие концепции высокого уровня:
- Служба. Служба GATT представляет собой набор данных и связанных с ними действий для выполнения определенной функции устройства. Например, монитор сердечного ритма обычно имеет по крайней мере одну «службу сердечного ритма». Информация о службе GATT содержится в объекте bluetoothLowEnergy.Service .
- Характеристика. Характеристика GATT — это базовый элемент данных, используемый для создания службы GATT, содержащий значение вместе со свойствами, определяющими способ доступа к этому значению. Например, «Сервис сердечного ритма» имеет характеристику «Измерение сердечного ритма», которая используется для получения значения частоты пульса пользователя. Информация о характеристике GATT содержится в объекте bluetoothLowEnergy.Characteristic .
- Дескриптор: Дескриптор характеристики ГАТТ содержит дополнительную информацию о характеристике. Информация о дескрипторе характеристики GATT содержится в объекте bluetoothLowEnergy.Descriptor .
API Bluetooth Low Energy позволяет приложениям находить информацию о службах, характеристиках и дескрипторах устройства, вызывая bluetoothLowEnergy.getServices , bluetoothLowEnergy.getCharacteristics и bluetoothLowEnergy.getDescriptors . Приложения могут фильтровать сервисы, характеристики и дескрипторы, сравнивая свое поле uuid
с желаемым UUID GATT:
chrome.bluetoothLowEnergy.getServices(deviceAddress, function(services) {
...
for (var i = 0; i < services.length; i++) {
if (services[i].uuid == HEART_RATE_SERVICE_UUID) {
heartRateService = services[i];
break;
}
}
...
});
Каждой услуге, характеристике и дескриптору, доступным через API, присваивается уникальный идентификатор экземпляра, который можно получить с помощью поля instanceId
. Этот идентификатор экземпляра можно использовать для идентификации объекта GATT и выполнения с ним определенных операций:
chrome.bluetoothLowEnergy.getCharacteristics(heartRateService.instanceId,
function(chracteristics) {
...
for (var i = 0; i < characteristics.length; i++) {
if (characteristics[i].uuid == HEART_RATE_MEASUREMENT_UUID) {
measurementChar = characteristics[i];
break;
}
}
...
chrome.bluetoothLowEnergy.getDescriptors(measurementChar.instanceId,
function(descriptors) {
...
});
});
Сервисные мероприятия
Как только устройство будет подключено, Chrome обнаружит его службы. По мере обнаружения и удаления каждой службы приложение будет получать события bluetoothLowEnergy.onServiceAdded и bluetoothLowEnergy.onServiceRemoved :
var initializeService = function(service) {
if (!service) {
console.log('No service selected!');
// Reset UI, etc.
...
return;
}
myService = service;
// Get all the characteristics and descriptors and bootstrap the app.
...
};
chrome.bluetoothLowEnergy.onServiceAdded.addListener(function(service) {
if (service.uuid == MY_SERVICE_UUID)
initializeService(service);
});
chrome.bluetoothLowEnergy.onServiceRemoved.addListener(function(service) {
if (service.instanceId == myService.instanceId)
initializeService(null);
});
Chrome асинхронно обнаруживает все характеристики и дескрипторы службы и отправляет событие bluetoothLowEnergy.onServiceAdded после завершения обнаружения. Если подключение к периферийному устройству прерывается, Chrome удаляет все связанные службы и отправляет событие bluetoothLowEnergy.onServiceRemoved .
Некоторые периферийные устройства могут модифицировать свои службы, например, могут изменяться характеристики службы или службы могут быть добавлены или полностью удалены. Chrome уведомляет приложения об этих изменениях с помощью событий bluetoothLowEnergy.onServiceChanged , bluetoothLowEnergy.onServiceAdded и bluetoothLowEnergy.onServiceRemoved .
chrome.bluetoothLowEnergy.onServiceChanged.addListener(function(service) {
if (service.instanceId != myService.instanceId)
return;
updateMyService(service);
});
Чтение и запись значения характеристики
Характеристика ГАТТ кодирует один аспект его услуги. Центральное приложение считывает, воздействует и изменяет состояние службы периферийного устройства, оперируя значением характеристики. Значение характеристики представляет собой последовательность байтов, и ее значение определяется спецификацией высокого уровня, определяющей определенную характеристику. Например, значение характеристики измерения сердечного ритма кодирует частоту сердечных сокращений пользователя и общее количество сожженных калорий, а значение характеристики расположения датчика на теле определяет, где на теле следует носить датчик сердечного ритма.
Chrome предоставляет метод bluetoothLowEnergy.readCharacteristicValue для считывания значения характеристики:
chrome.bluetoothLowEnergy.readCharacteristicValue(chrc.instanceId,
function(result) {
if (chrome.runtime.lastError) {
console.log('Failed to read value: ' + chrome.runtime.lastError.message);
return;
}
var bytes = new Uint8Array(result.value);
// Do stuff with the bytes.
...
});
Некоторые характеристики доступны для записи, особенно те, которые ведут себя как «контрольные точки», где запись значения имеет побочные эффекты. Например, характеристика контрольной точки сердечного ритма используется для указания датчику сердечного ритма сбросить счетчик общего количества сожженных калорий и поддерживает только запись. Для этого в Chrome предусмотрен метод bluetoothLowEnergy.writeCharacteristicValue :
var myBytes = new Uint8Array([ ... ]);
chrome.bluetoothLowEnergy.writeCharacteristicValue(chrc.instanceId,
myBytes.buffer,
function() {
if (chrome.runtime.lastError) {
console.log('Failed to write value: ' +
chrome.runtime.lastError.message);
return;
}
// Value is written now.
});
Дескрипторы характеристик ведут себя одинаково и могут быть доступны для чтения и/или записи. Chrome предоставляет методы bluetoothLowEnergy.readDescriptorValue и bluetoothLowEnergy.writeDescriptorValue для чтения и записи значения дескриптора.
Чтобы проверить, поддерживает ли характеристика чтение или запись, приложение может проверить поле properties
объекта bluetoothLowEnergy.Characteristic . Хотя это поле не содержит информации о требованиях безопасности для доступа к значению, оно описывает, какую операцию со значением в целом поддерживает характеристика.
Обработка уведомлений о значениях
Некоторые характеристики сообщают о своем значении с помощью уведомлений или указаний. Например, характеристика измерения сердечного ритма недоступна ни для чтения, ни для записи, но отправляет обновления своего текущего значения через регулярные промежутки времени. Приложения могут прослушивать эти уведомления с помощью события bluetoothLowEnergy.onCharacteristicValueChanged .
chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(
function(chrc) {
if (chrc.instanceId != myCharId)
return;
var bytes = new Uint8Array(chrc.value);
// Do stuff with the bytes.
...
});
Даже если характеристика поддерживает уведомления/индикации, по умолчанию они не включены. Приложение должно вызывать методы bluetoothLowEnergy.startCharacteristicNotifications и bluetoothLowEnergy.stopCharacteristicNotifications, чтобы начать или прекратить получение события bluetoothLowEnergy.onCharacteristicValueChanged .
// Start receiving characteristic value notifications.
var notifying = false;
chrome.bluetoothLowEnergy.startCharacteristicNotifications(chrc.instanceId,
function() {
if (chrome.runtime.lastError) {
console.log('Failed to enable notifications: ' +
chrome.runtime.lastError.message);
return;
}
notifying = true;
});
...
// No longer interested in notifications from this characteristic.
if (notifying) {
chrome.bluetoothLowEnergy.stopCharacteristicNotifications(
chrc.instanceId);
}
После запуска уведомлений приложение будет получать bluetoothLowEnergy.onCharacteristicValueChanged каждый раз, когда от характеристики будет получено уведомление или указание. Если характеристика поддерживает чтение, то это событие также будет отправлено после успешного вызова bluetoothLowEnergy.readCharacteristicValue . Это позволяет приложениям унифицировать поток управления обновлением значений, запускаемым посредством запроса на чтение и уведомлений:
chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(
function(chrc) {
// Process the value.
...
});
chrome.bluetoothLowEnergy.startCharacteristicNotifications(chrc.instanceId,
function() {
// Notifications started. Read the initial value.
chrome.bluetoothLowEnergy.readCharacteristicValue(chrc.instanceId,
function(result) {
...
// No need to do anything here since onCharacteristicValueChanged
// will handle it.
});
});
Если характеристика поддерживает уведомления, поле ее properties
будет содержать свойство "notify"
или "indicate"
.
ПРИМЕЧАНИЕ. Если характеристика поддерживает уведомления/индикации, она будет иметь дескриптор «Конфигурация характеристики клиента» для включения/отключения уведомлений. Chrome не разрешает приложениям выполнять запись в этот дескриптор. Вместо этого приложения должны использовать методы bluetoothLowEnergy.startCharacteristicNotifications и bluetoothLowEnergy.stopCharacteristicNotifications для управления поведением уведомлений.