diff --git a/front/src/components/app.jsx b/front/src/components/app.jsx
index a20f73c6b3..f2df84de98 100644
--- a/front/src/components/app.jsx
+++ b/front/src/components/app.jsx
@@ -89,6 +89,7 @@ import BluetoothDevicePage from '../routes/integration/all/bluetooth/device-page
import BluetoothEditDevicePage from '../routes/integration/all/bluetooth/edit-page';
import BluetoothSetupPage from '../routes/integration/all/bluetooth/setup-page';
import BluetoothSetupPeripheralPage from '../routes/integration/all/bluetooth/setup-page/setup-peripheral';
+import BluetoothSettingsPage from '../routes/integration/all/bluetooth/settings-page';
// EweLink
import EweLinkPage from '../routes/integration/all/ewelink/device-page';
@@ -205,6 +206,7 @@ const AppRouter = connect(
+
diff --git a/front/src/config/demo.json b/front/src/config/demo.json
index 5a92b7a060..3cadf9dddc 100644
--- a/front/src/config/demo.json
+++ b/front/src/config/demo.json
@@ -1668,6 +1668,12 @@
"name": "bluetooth",
"enabled": true
},
+ "get /api/v1/service/bluetooth/config": {
+ "presenceScanner": {
+ "status": "enabled",
+ "frequency": 60000
+ }
+ },
"get /api/v1/service/bluetooth/device": [
{
"id": "fbedb47f-4d25-4381-8923-2633b23192a0",
@@ -1723,7 +1729,7 @@
}
},
"get /api/v1/service/bluetooth/status": {
- "running": true
+ "ready": true
},
"get /api/v1/service/bluetooth/peripheral": [
{
diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json
index d3c4616847..5f51dafe39 100644
--- a/front/src/config/i18n/en.json
+++ b/front/src/config/i18n/en.json
@@ -644,7 +644,8 @@
"title": "Bluetooth",
"description": "Control your Bluetooth devices.",
"deviceTab": "Devices",
- "setupTab": "Setup",
+ "discoverTab": "Discover",
+ "setupTab": "Presence scanner",
"bluetoothNotReadyError": "Bluetooth device is not reachable, please check it is enabled.",
"device": {
"title": "Bluetooth Devices",
@@ -657,6 +658,7 @@
"externalIdLabel": "External ID",
"manufacturerLabel": "Manufacturer",
"modelLabel": "Model",
+ "presenceSensorLabel": "Use this device as presence sensor",
"featuresLabel": "Features",
"noFeatureDiscovered": "No features discovered.",
"featureNamePlaceholder": "Enter feature name",
@@ -664,10 +666,9 @@
"deleteButton": "Delete",
"editButton": "Edit"
},
- "setup": {
- "title": "Bluetooth Setup",
+ "discover": {
+ "title": "Bluetooth Discover",
"scanButton": "Scan",
- "reloadButton": "Reload",
"noDeviceFound": "No bluetooth device discovered.",
"createDeviceInGladys": "Connect in Gladys",
"updateDeviceInGladys": "Update in Gladys",
@@ -684,6 +685,18 @@
"cancelLabel": "Cancel",
"successLabel": "Done"
}
+ },
+ "setup": {
+ "title": "Presence scanner",
+ "noConfigLabel": "Configuration not loaded, please retry.",
+ "errorLabel": "An error occurred while loading configuration.",
+ "presenceScannerDescription": "The presence scanner feature is doing a bluetooth scan at a defined interval. When a device is detected, the device will be flagged as \"seen\" in Gladys. In scene, you can create a trigger on this state change to set you home when a device is detected at home for example.",
+ "presenceScannerStatusLabel": "Enable or disable presence scanner",
+ "presenceScannerFrequencyLabel": "Scanner interval",
+ "presenceScannerFrequencyUnit": "minutes",
+ "presenceScannerFrequencyError": "Only integers are accepted.",
+ "presenceScannerButton": "Scan device presence now",
+ "saveLabel": "Save configuration"
}
},
"eWeLink": {
@@ -810,6 +823,15 @@
"userSeenDescription": "This action set the user as \"at home\".",
"userLeftHomeDescription": "This action set the user as \"left home\"."
},
+ "checkUserPresence": {
+ "description": "When this action is executed, Gladys checks if one of the selected device was seen at home in the last X minutes. If one of the device was seen, the action does nothing more. If no device was seen, the user is marked as \"left home\".",
+ "userLabel": "User",
+ "houseLabel": "House",
+ "deviceLabel": "Devices",
+ "minutesLabel": "Devices seen less than",
+ "minutesPlaceholder": "Duration (in minutes)",
+ "minutes": "minutes"
+ },
"httpRequest": {
"description": "This action let you make HTTP requests.",
"methodLabel": "Method",
@@ -855,7 +877,8 @@
},
"user": {
"set-seen-at-home": "User seen at home",
- "set-out-of-home": "User left home"
+ "set-out-of-home": "User left home",
+ "check-presence": "Check user presence"
},
"http": {
"request": "Make a HTTP request"
@@ -887,6 +910,7 @@
"valuePlaceholder": "Value",
"on": "On",
"off": "Off",
+ "deviceSeen": "If the device is detected",
"onlyExecuteAtThreshold": "Execute only when threshold is passed (and not at every value sent by the device)"
},
"scheduledTrigger": {
diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json
index e780af8c76..710b5c9347 100644
--- a/front/src/config/i18n/fr.json
+++ b/front/src/config/i18n/fr.json
@@ -308,7 +308,8 @@
"title": "Bluetooth",
"description": "Contrôler vos appareils Bluetooth.",
"deviceTab": "Appareils",
- "setupTab": "Configuration",
+ "discoverTab": "Découverte",
+ "setupTab": "Scanner de présence",
"bluetoothNotReadyError": "Le module Bluetooth n'est pas disponible, merci de vérifier qu'il est bien activé.",
"device": {
"title": "Appareils Bluetooth",
@@ -321,6 +322,7 @@
"externalIdLabel": "ID externe",
"manufacturerLabel": "Fabricant",
"modelLabel": "Modèle",
+ "presenceSensorLabel": "Utiliser cet appareil comme un détecteur de présence",
"featuresLabel": "Fonctionnalités",
"noFeatureDiscovered": "Aucune fonctionnalité détectée.",
"featureNamePlaceholder": "Nom de la fonctionnalité",
@@ -328,10 +330,9 @@
"deleteButton": "Supprimer",
"editButton": "Editer"
},
- "setup": {
- "title": "Configuration Bluetooth",
+ "discover": {
+ "title": "Découverte Bluetooth",
"scanButton": "Rechercher",
- "reloadButton": "Recharger",
"noDeviceFound": "Aucun périphérique Bluetooth détecté.",
"createDeviceInGladys": "Connecter dans Gladys",
"updateDeviceInGladys": "Mettre à jour dans Gladys",
@@ -348,6 +349,18 @@
"cancelLabel": "Annuler",
"successLabel": "Effectué"
}
+ },
+ "setup": {
+ "title": "Scanner de présence",
+ "noConfigLabel": "La configuration n'est pas chargée, merci de réessayer.",
+ "errorLabel": "Une erreur est survenue lors du chargement de la configuration.",
+ "presenceScannerDescription": "Le scanner de présence effectue un scan Bluetooth à interval régulier. Dès qu’un appareil est détecté, l'appareil est marqué comme \"présent\" dans Gladys. Dans les scènes, vous avez la possibilité de créer un déclencheur qui écoute sur cet état. Ainsi, vous pouvez facilement faire une scène qui vous met comme présent à la maison dès qu'un appareil bluetooth est détecté.",
+ "presenceScannerStatusLabel": "Activer ou désactiver l'analyse de présence d'appareils",
+ "presenceScannerFrequencyLabel": "Fréquence de scan bluetooth",
+ "presenceScannerFrequencyUnit": "minutes",
+ "presenceScannerFrequencyError": "Seul un nombre entier est autorisé.",
+ "presenceScannerButton": "Scanner la présence maintenant",
+ "saveLabel": "Enregistrer"
}
},
"telegram": {
@@ -810,6 +823,15 @@
"userSeenDescription": "Cette action marque l'utilisateur comme présent à la maison.",
"userLeftHomeDescription": "Cette action indique que l'utilisateur a quitté la maison."
},
+ "checkUserPresence": {
+ "description": "Lorsque cette action est exécutée, Gladys va regarder si les appareils sélectionnées ont été détectés dans les dernières minutes ( selon le nombre de minutes sélectionnée ci-dessous ). Si un appareil a été détecté, Gladys ne fera rien. Si aucun n'appareil n'a été détecté, Gladys marquera l'utilisateur comme hors de la maison.",
+ "userLabel": "Utilisateur",
+ "houseLabel": "Maison",
+ "deviceLabel": "Appareils",
+ "minutesLabel": "Appareil vu il y a moins de",
+ "minutesPlaceholder": "Durée (en minutes)",
+ "minutes": "minutes"
+ },
"httpRequest": {
"description": "Cette action permet d'envoyer une requête HTTP.",
"methodLabel": "Méthode",
@@ -855,7 +877,8 @@
},
"user": {
"set-seen-at-home": "Utilisateur vu à la maison",
- "set-out-of-home": "Utilisateur parti de la maison"
+ "set-out-of-home": "Utilisateur parti de la maison",
+ "check-presence": "Vérifier la présence"
},
"http": {
"request": "Faire une requête HTTP"
@@ -887,6 +910,7 @@
"valuePlaceholder": "Valeur",
"on": "On",
"off": "Off",
+ "deviceSeen": "Si l'appareil est détecté",
"onlyExecuteAtThreshold": "Exécuter seulement lorsque le seuil est passé ( et non pas à chaque valeur envoyée )"
},
"scheduledTrigger": {
diff --git a/front/src/routes/integration/all/bluetooth/BluetoothPage.js b/front/src/routes/integration/all/bluetooth/BluetoothPage.js
index 28dff14fca..7e94190517 100644
--- a/front/src/routes/integration/all/bluetooth/BluetoothPage.js
+++ b/front/src/routes/integration/all/bluetooth/BluetoothPage.js
@@ -19,7 +19,7 @@ const BluetoothPage = ({ children, ...props }) => (
class="list-group-item list-group-item-action d-flex align-items-center"
>
-
+
@@ -30,7 +30,18 @@ const BluetoothPage = ({ children, ...props }) => (
class="list-group-item list-group-item-action d-flex align-items-center"
>
-
+
+
+
+
+
+
+
+
diff --git a/front/src/routes/integration/all/bluetooth/settings-page/BluetoothPresenceScanner.jsx b/front/src/routes/integration/all/bluetooth/settings-page/BluetoothPresenceScanner.jsx
new file mode 100644
index 0000000000..884cdafea7
--- /dev/null
+++ b/front/src/routes/integration/all/bluetooth/settings-page/BluetoothPresenceScanner.jsx
@@ -0,0 +1,82 @@
+import { Component } from 'preact';
+import { Text } from 'preact-i18n';
+import cx from 'classnames';
+
+import { PRESENCE_STATUS } from '../../../../../../../server/services/bluetooth/lib/utils/bluetooth.constants';
+
+class BluetoothPresenceScanner extends Component {
+ toggleStatus = () => {
+ const newStatus =
+ this.props.config.status === PRESENCE_STATUS.ENABLED ? PRESENCE_STATUS.DISABLED : PRESENCE_STATUS.ENABLED;
+ this.props.updateConfig('presenceScanner', 'status', newStatus);
+ };
+
+ updateFrequency = e => {
+ const { value } = e.target;
+ const rawFrequency = parseFloat(value, 10);
+
+ if (Number.isInteger(rawFrequency) && rawFrequency >= 1) {
+ this.props.updateConfig('presenceScanner', 'frequency', rawFrequency * 60000);
+ this.setState({ frequencyError: undefined });
+ } else {
+ this.setState({ frequencyError: value });
+ }
+ };
+
+ render({ config = {}, disabled }, { frequencyError }) {
+ const enabled = config.status === PRESENCE_STATUS.ENABLED;
+ const frequency = config.frequency / 60000;
+
+ return (
+