diff --git a/lib/units/device/index.js b/lib/units/device/index.js index 6f50f5ec..a56985b1 100644 --- a/lib/units/device/index.js +++ b/lib/units/device/index.js @@ -38,6 +38,7 @@ module.exports = function(options) { .dependency(require('./plugins/account')) .dependency(require('./plugins/ringer')) .dependency(require('./plugins/wifi')) + .dependency(require('./plugins/bluetooth')) .dependency(require('./plugins/sd')) .dependency(require('./plugins/filesystem')) .define(function(options, heartbeat, solo) { diff --git a/lib/units/device/plugins/bluetooth.js b/lib/units/device/plugins/bluetooth.js new file mode 100644 index 00000000..85083c68 --- /dev/null +++ b/lib/units/device/plugins/bluetooth.js @@ -0,0 +1,53 @@ +var syrup = require('stf-syrup') + +var logger = require('../../../util/logger') +var wire = require('../../../wire') +var wireutil = require('../../../wire/util') + +module.exports = syrup.serial() + .dependency(require('./service')) + .dependency(require('../support/router')) + .dependency(require('../support/push')) + .define(function(options, service, router, push) { + var log = logger.createLogger('device:plugins:bluetooth') + + router.on(wire.BluetoothSetEnabledMessage, function(channel, message) { + var reply = wireutil.reply(options.serial) + log.info('Setting Bluetooth "%s"', message.enabled) + service.setBluetoothEnabled(message.enabled) + .timeout(30000) + .then(function() { + push.send([ + channel + , reply.okay() + ]) + }) + .catch(function(err) { + log.error('Setting Bluetooth enabled failed', err.stack) + push.send([ + channel + , reply.fail(err.message) + ]) + }) + }) + + router.on(wire.BluetoothGetStatusMessage, function(channel) { + var reply = wireutil.reply(options.serial) + log.info('Getting Bluetooth status') + service.getBluetoothStatus() + .timeout(30000) + .then(function(enabled) { + push.send([ + channel + , reply.okay(enabled ? 'bluetooth_enabled' : 'bluetooth_disabled') + ]) + }) + .catch(function(err) { + log.error('Getting Bluetooth status failed', err.stack) + push.send([ + channel + , reply.fail(err.message) + ]) + }) + }) + }) diff --git a/lib/units/device/plugins/service.js b/lib/units/device/plugins/service.js index 28136c9a..f3ec8a8b 100644 --- a/lib/units/device/plugins/service.js +++ b/lib/units/device/plugins/service.js @@ -675,6 +675,35 @@ module.exports = syrup.serial() }) } + plugin.setBluetoothEnabled = function(enabled) { + return runServiceCommand( + apk.wire.MessageType.SET_BLUETOOTH_ENABLED + , new apk.wire.SetBluetoothEnabledRequest(enabled) + ) + .timeout(10000) + .then(function(data) { + var response = apk.wire.SetBluetoothEnabledResponse.decode(data) + if (!response.success) { + throw new Error('Unable to set Bluetooth') + } + }) + } + + plugin.getBluetoothStatus = function() { + return runServiceCommand( + apk.wire.MessageType.GET_BLUETOOTH_STATUS + , new apk.wire.GetBluetoothStatusRequest() + ) + .timeout(10000) + .then(function(data) { + var response = apk.wire.GetBluetoothStatusResponse.decode(data) + if (response.success) { + return response.status + } + throw new Error('Unable to get Bluetooth status') + }) + } + plugin.getSdStatus = function() { return runServiceCommand( apk.wire.MessageType.GET_SD_STATUS diff --git a/lib/units/websocket/index.js b/lib/units/websocket/index.js index b7071d03..4443eb31 100644 --- a/lib/units/websocket/index.js +++ b/lib/units/websocket/index.js @@ -766,6 +766,26 @@ module.exports = function(options) { ) ]) }) + .on('bluetooth.set', function(channel, responseChannel, data) { + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.BluetoothSetEnabledMessage(data.enabled) + ) + ]) + }) + .on('bluetooth.get', function(channel, responseChannel) { + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.BluetoothGetStatusMessage() + ) + ]) + }) .on('group.invite', function(channel, responseChannel, data) { joinChannel(responseChannel) push.send([ diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index 3bc8cdf5..d7f903ec 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -82,6 +82,8 @@ enum MessageType { FileSystemGetMessage = 82; ConnectStartedMessage = 92; ConnectStoppedMessage = 93; + BluetoothSetEnabledMessage = 94; + BluetoothGetStatusMessage = 95; GroupUserChangeMessage = 1200; DeviceGroupChangeMessage = 1201; DeviceOriginGroupMessage = 1202; @@ -711,6 +713,13 @@ message WifiSetEnabledMessage { message WifiGetStatusMessage { } +message BluetoothSetEnabledMessage { + required bool enabled = 1; +} + +message BluetoothGetStatusMessage { +} + // Events, these must be kept in sync with STFService/wire.proto message AirplaneModeEvent { diff --git a/res/app/components/stf/control/control-service.js b/res/app/components/stf/control/control-service.js index a274030a..fa389661 100644 --- a/res/app/components/stf/control/control-service.js +++ b/res/app/components/stf/control/control-service.js @@ -293,6 +293,16 @@ module.exports = function ControlServiceFactory( return sendTwoWay('wifi.get') } + this.setBluetoothEnabled = function(enabled) { + return sendTwoWay('bluetooth.set', { + enabled: enabled + }) + } + + this.getBluetoothStatus = function() { + return sendTwoWay('bluetooth.get') + } + window.cc = this } diff --git a/res/app/control-panes/automation/device-settings/device-settings-controller.js b/res/app/control-panes/automation/device-settings/device-settings-controller.js index 22de39d2..db9da2b0 100644 --- a/res/app/control-panes/automation/device-settings/device-settings-controller.js +++ b/res/app/control-panes/automation/device-settings/device-settings-controller.js @@ -1,5 +1,7 @@ module.exports = function DeviceSettingsCtrl($scope, $timeout) { $scope.wifiEnabled = true + $scope.bluetoothEnabled = true + $scope.bluetoothPending = false function getWifiStatus() { if ($scope.control) { @@ -19,6 +21,35 @@ module.exports = function DeviceSettingsCtrl($scope, $timeout) { } } + function getBluetoothStatus() { + if ($scope.control) { + $scope.bluetoothPending = true + $scope.control.getBluetoothStatus() + .then(function(result) { + $scope.$apply(function() { + $scope.bluetoothEnabled = (result.lastData === 'bluetooth_enabled') + }) + }) + .finally(function() { + $scope.bluetoothPending = false + }) + } + } + getBluetoothStatus() + + $scope.toggleBluetooth = function(enable) { + if ($scope.control) { + $scope.bluetoothPending = true + $scope.control.setBluetoothEnabled(enable) + .then(function() { + $scope.bluetoothEnabled = enable + }) + .finally(function() { + $scope.bluetoothPending = false + }) + } + } + $scope.$watch('ringerMode', function(newValue, oldValue) { if (oldValue) { if ($scope.control) { diff --git a/res/app/control-panes/automation/device-settings/device-settings.pug b/res/app/control-panes/automation/device-settings/device-settings.pug index 6e31287f..b0a359ae 100644 --- a/res/app/control-panes/automation/device-settings/device-settings.pug +++ b/res/app/control-panes/automation/device-settings/device-settings.pug @@ -4,7 +4,7 @@ span(translate) Device Settings .widget-content.padded .row - .col-md-6 + .col-md-4 h6(translate) Manner Mode .btn-group label.btn.btn-sm.btn-primary-outline(ng-model='ringerMode', uib-btn-radio='"SILENT"', uib-tooltip='{{"Silent Mode" | translate}}') @@ -14,7 +14,7 @@ label.btn.btn-sm.btn-primary-outline(ng-model='ringerMode', uib-btn-radio='"NORMAL"', uib-tooltip='{{"Normal Mode" | translate}}') i.fa.fa-volume-up.fa-fw - .col-md-6 + .col-md-4 h6(translate) WiFi .btn-group label.btn.btn-sm.btn-primary-outline(ng-model='wifiEnabled', ng-click='toggleWifi(false)', uib-btn-radio='false', uib-tooltip='{{"Disable WiFi" | translate}}') @@ -22,6 +22,14 @@ label.btn.btn-sm.btn-primary-outline(ng-model='wifiEnabled', ng-click='toggleWifi(true)', uib-btn-radio='true', uib-tooltip='{{"Enable WiFi" | translate}}') i.fa.fa-wifi.fa-fw + .col-md-4 + h6(translate) Bluetooth + .btn-group + label.btn.btn-sm.btn-primary-outline(ng-model='bluetoothEnabled', ng-click='toggleBluetooth(false)', ng-disabled='bluetoothPending', uib-btn-radio='false', uib-tooltip='{{"Disable Bluetooth" | translate}}') + i.fa.fa-power-off.fa-fw + label.btn.btn-sm.btn-primary-outline(ng-model='bluetoothEnabled', ng-click='toggleBluetooth(true)', ng-disabled='bluetoothPending', uib-btn-radio='true', uib-tooltip='{{"Enable Bluetooth" | translate}}') + i.fa.fa-bluetooth-b.fa-fw + //.row .col-md-12 h6(translate) Lock Rotation