diff --git a/front/src/components/boxs/device-in-room/DeviceRow.jsx b/front/src/components/boxs/device-in-room/DeviceRow.jsx index 1edd1f5f72..5f3f0dd3f6 100644 --- a/front/src/components/boxs/device-in-room/DeviceRow.jsx +++ b/front/src/components/boxs/device-in-room/DeviceRow.jsx @@ -14,6 +14,7 @@ import ThermostatDeviceFeature from './device-features/ThermostatDeviceFeature'; import AirConditioningModeDeviceFeature from './device-features/AirConditioningModeDeviceFeature'; import PilotWireModeDeviceFeature from './device-features/PilotWireModeDeviceFeature'; import LMHVolumeDeviceFeature from './device-features/LMHVolumeDeviceFeature'; +import PushDeviceFeature from './device-features/PushDeviceFeature'; const ROW_TYPE_BY_FEATURE_TYPE = { [DEVICE_FEATURE_TYPES.LIGHT.BINARY]: BinaryDeviceFeature, @@ -33,7 +34,8 @@ const ROW_TYPE_BY_FEATURE_TYPE = { [DEVICE_FEATURE_TYPES.HEATER.PILOT_WIRE_MODE]: PilotWireModeDeviceFeature, [DEVICE_FEATURE_TYPES.SIREN.LMH_VOLUME]: LMHVolumeDeviceFeature, [DEVICE_FEATURE_TYPES.SIREN.MELODY]: NumberDeviceFeature, - [DEVICE_FEATURE_TYPES.DURATION.DECIMAL]: MultiLevelDeviceFeature + [DEVICE_FEATURE_TYPES.DURATION.DECIMAL]: MultiLevelDeviceFeature, + [DEVICE_FEATURE_TYPES.BUTTON.PUSH]: PushDeviceFeature }; const DeviceRow = ({ children, ...props }) => { diff --git a/front/src/components/boxs/device-in-room/SupportedFeatureTypes.jsx b/front/src/components/boxs/device-in-room/SupportedFeatureTypes.jsx index eeb2419339..5cb8f82ba4 100644 --- a/front/src/components/boxs/device-in-room/SupportedFeatureTypes.jsx +++ b/front/src/components/boxs/device-in-room/SupportedFeatureTypes.jsx @@ -16,7 +16,8 @@ const SUPPORTED_FEATURE_TYPES = [ DEVICE_FEATURE_TYPES.HEATER.PILOT_WIRE_MODE, DEVICE_FEATURE_TYPES.SIREN.LMH_VOLUME, DEVICE_FEATURE_TYPES.SIREN.MELODY, - DEVICE_FEATURE_TYPES.DURATION.DECIMAL + DEVICE_FEATURE_TYPES.DURATION.DECIMAL, + DEVICE_FEATURE_TYPES.BUTTON.PUSH ]; export default SUPPORTED_FEATURE_TYPES; diff --git a/front/src/components/boxs/device-in-room/device-features/PushDeviceFeature.jsx b/front/src/components/boxs/device-in-room/device-features/PushDeviceFeature.jsx new file mode 100644 index 0000000000..30791fba6d --- /dev/null +++ b/front/src/components/boxs/device-in-room/device-features/PushDeviceFeature.jsx @@ -0,0 +1,44 @@ +import { Component } from 'preact'; +import cx from 'classnames'; +import { Text } from 'preact-i18n'; +import style from './style.css'; + +class PushDeviceComponent extends Component { + constructor(props) { + super(props); + this.state = { + loading: false + }; + } + push = async () => { + await this.setState({ loading: true }); + this.props.updateValue(this.props.deviceFeature, 1); + setTimeout(() => { + this.setState({ loading: false }); + }, 350); + }; + + render(props, { loading }) { + return ( + + + + + {props.rowName} + + + + + ); + } +} + +export default PushDeviceComponent; diff --git a/front/src/components/boxs/device-in-room/device-features/style.css b/front/src/components/boxs/device-in-room/device-features/style.css index b71eaf53e1..e2135f162e 100644 --- a/front/src/components/boxs/device-in-room/device-features/style.css +++ b/front/src/components/boxs/device-in-room/device-features/style.css @@ -47,3 +47,9 @@ input[type='range'][class~='light-temperature']::-ms-fill-lower { display: none; } } + +.btnLoading { + &::after { + border: 2px solid #5eba00; + } +} diff --git a/front/src/config/i18n/de.json b/front/src/config/i18n/de.json index baa632d928..2471acf8ec 100644 --- a/front/src/config/i18n/de.json +++ b/front/src/config/i18n/de.json @@ -298,7 +298,8 @@ "deviceTitle": "{{name}} - {{type}}", "addButton": "+", "substractButton": "-", - "motionDetected": "Bewegung erkannt" + "motionDetected": "Bewegung erkannt", + "pushButton": "Schieben" }, "devices": { "editDeviceFeaturesLabel": "Wähle die Geräte aus, die du anzeigen möchtest:", @@ -2035,6 +2036,13 @@ "variablesExplanation": "Um eine Variable im Text einzufügen, gib \"{{\" ein. Um einen Variablenwert festzulegen, musst du vor diesem Feld das das Feld \"Gerätewert abrufen\" verwenden.", "messagePlaceholder": "Meine Message" }, + "zigbee2mqttMessage": { + "topic": "Topic", + "topicPlaceholder": "/gladys/mein-topic", + "messageLabel": "Message", + "variablesExplanation": "Um eine Variable im Text einzufügen, gib \"{{\" ein. Um einen Variablenwert festzulegen, musst du vor diesem Feld das das Feld \"Gerätewert abrufen\" verwenden.", + "messagePlaceholder": "Meine Message" + }, "playNotification": { "description": "Diese Aktion lässt Gladys auf dem ausgewählten Lautsprecher sprechen.", "needGladysPlus": "Erfordert Gladys Plus, da die Text-to-Speech-APIs kostenpflichtig sind.", @@ -2103,6 +2111,9 @@ "mqtt": { "send": "MQTT-Message senden" }, + "zigbee2mqtt": { + "send": "Zigbee2mqtt-Message senden" + }, "music": { "play-notification": "Auf einem Lautsprecher sprechen" }, @@ -2134,8 +2145,8 @@ "sunset": "Jeder Sonnenuntergang" }, "user": { - "back-home": "Benutzer hat das Zuhause verlassen", - "left-home": "Benutzer ist zurück zu Hause" + "back-home": "Benutzer ist zurück zu Hause", + "left-home": "Benutzer hat das Zuhause verlassen" }, "house": { "empty": "Das Zuhause ist leer", @@ -2175,7 +2186,8 @@ "on": "Ein", "off": "Aus", "deviceSeen": "Wenn das Gerät erkannt wird", - "onlyExecuteAtThreshold": "Nur ausführen, wenn der Schwellenwert überschritten wird (und nicht bei jedem vom Gerät gesendeten Wert)" + "onlyExecuteAtThreshold": "Nur ausführen, wenn der Schwellenwert überschritten wird (und nicht bei jedem vom Gerät gesendeten Wert)", + "activateOrDeactivateForDuration": "Szene ausführen, nachdem die Bedingung für die Dauer gültig war:" }, "scheduledTrigger": { "typeLabel": "Typ", @@ -2929,7 +2941,15 @@ "48": "Langer Druck", "49": "Kurzes Loslassen", "50": "Langes Loslassen", - "51": "Doppeltes Drücken" + "51": "Doppeltes Drücken", + "52": "Umschalten", + "53": "Umschalten Gehalten", + "54": "Helligkeit Auf-Taste gedrückt", + "55": "Helligkeit Auf-Taste gehalten", + "56": "Helligkeit Auf-Taste Loslassen", + "57": "Helligkeit Ab-Taste gedrückt", + "58": "Helligkeit Ab-Taste gehalten", + "59": "Helligkeit Ab-Taste Loslassen" } }, "heater": { @@ -3095,7 +3115,8 @@ }, "button": { "shortCategoryName": "Schaltfläche", - "click": "Schaltfläche (Klick)" + "click": "Schaltfläche (Klick)", + "push": "Druckknopf" }, "pressure-sensor": { "shortCategoryName": "Drucksensor", @@ -3199,6 +3220,67 @@ "index": "Index", "daily-consumption": "Tagesverbrauch" }, + "teleinformation": { + "shortCategoryName": "Teleinformation", + "binary": "Schalter", + "east": "Gesamt abgegebene Wirkleistung (EAST)", + "eait": "Gesamt eingespeiste Wirkleistung (EAIT)", + "easf01": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF01)", + "easf02": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF02)", + "easf03": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF03)", + "easf04": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF04)", + "easf05": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF05)", + "easf06": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF06)", + "easf07": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF07)", + "easf08": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF08)", + "easf09": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF09)", + "easf10": "Gesamt abgegebene Wirkleistung vom Anbieter (EASF10)", + "pref": "Referenz-Scheinleistung (PREF)", + "pcoup": "Scheinleistungsschwelle (PCOUP)", + "vtic": "Protokollversion (VTIC)", + "ccasn": "Aktueller Punkt der entnommenen Lastkurve (CCASN)", + "ccasn_1": "Vorheriger Punkt der entnommenen Lastkurve (CCASN-1)", + "umoy1": "Durchschnittliche Effektivspannung (UMOY1)", + "umoy2": "Durchschnittliche Effektivspannung (UMOY2)", + "umoy3": "Durchschnittliche Effektivspannung (UMOY3)", + "erq1": "Gesamt Blindleistung Q1 (ERQ1)", + "erq2": "Gesamt Blindleistung Q2 (ERQ2)", + "erq3": "Gesamt Blindleistung Q3 (ERQ3)", + "erq4": "Gesamt Blindleistung Q4 (ERQ4)", + "irms1": "Effektivstrom (IRMS1)", + "irms2": "Effektivstrom (IRMS2)", + "irms3": "Effektivstrom (IRMS3)", + "urms1": "Effektivspannung (URMS1)", + "urms2": "Effektivspannung (URMS2)", + "urms3": "Effektivspannung (URMS3)", + "easd01": "Entnommene Wirkenergie des Verteilers (EASD01)", + "easd02": "Entnommene Wirkenergie des Verteilers (EASD02)", + "easd03": "Entnommene Wirkenergie des Verteilers (EASD03)", + "easd04": "Entnommene Wirkenergie des Verteilers (EASD04)", + "ntarf": "Tarif-Indexnummer (NTARF)", + "ccain": "Punkt n der entnommenen Lastkurve (CCAIN)", + "ccain_1": "Punkt n-1 der entnommenen Lastkurve (CCAIN-1)", + "sinsti": "Momentan eingespeiste Scheinleistung (SINSTI)", + "smaxin": "Max. eingespeiste Scheinleistung n (SMAXIN)", + "smaxin_1": "Max. eingespeiste Scheinleistung n-1 (SMAXIN-1)", + "smaxn": "Max. abgegebene Scheinleistung (SMAXN)", + "smaxn2": "Max. abgegebene Scheinleistung (SMAXN2)", + "smaxn3": "Max. abgegebene Scheinleistung (SMAXN3)", + "sinsts": "Momentane abgegebene Scheinleistung (SINSTS)", + "sinsts2": "Momentane abgegebene Scheinleistung (SINSTS2)", + "sinsts3": "Momentane abgegebene Scheinleistung (SINSTS3)", + "smaxn_1": "Max. entnommene Scheinleistung n-1 (SMAXN-1)", + "smaxn2_1": "Max. entnommene Scheinleistung n-1 (SMAXN2-1)", + "smaxn3_1": "Max. entnommene Scheinleistung n-1 (SMAXN3-1)", + "hhphc": "HPHC-Programm (HHPHC)", + "imax": "Spitzenstrom (IMAX)", + "adps": "Warnung bei Überschreitung der Vertragsleistung (ADPS)", + "imax2": "Spitzenstrom (IMAX2)", + "imax3": "Spitzenstrom (IMAX3)", + "adir1": "Überstromwarnung (ADIR1)", + "adir2": "Überstromwarnung (ADIR2)", + "adir3": "Überstromwarnung (ADIR3)" + }, "volume-sensor": { "shortCategoryName": "Lautstärke-Sensor", "decimal": "Lautstärke (dezimal)", diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 4e0d9a086c..bf7f24fce2 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -298,7 +298,8 @@ "deviceTitle": "{{name}} - {{type}}", "addButton": "+", "substractButton": "-", - "motionDetected": "Motion detected" + "motionDetected": "Motion detected", + "pushButton": "Push" }, "devices": { "editDeviceFeaturesLabel": "Select the devices you want to display:", @@ -2035,6 +2036,13 @@ "variablesExplanation": "To inject a variable in the text, press '{{ '. To set a variable value, you need to use the 'Get device value' box before this one.", "messagePlaceholder": "My message" }, + "zigbee2mqttMessage": { + "topic": "Topic", + "topicPlaceholder": "/gladys/my-topic", + "messageLabel": "Message", + "variablesExplanation": "To inject a variable in the text, press '{{ '. To set a variable value, you need to use the 'Get device value' box before this one.", + "messagePlaceholder": "My message" + }, "playNotification": { "description": "This action will make Gladys speak on the selected speaker.", "needGladysPlus": "Requires Gladys Plus as Text-To-Speech APIs are paid.", @@ -2103,6 +2111,9 @@ "mqtt": { "send": "Send MQTT Message" }, + "zigbee2mqtt": { + "send": "Send Zigbee2mqtt Message" + }, "music": { "play-notification": "Talk on a speaker" }, @@ -2134,8 +2145,8 @@ "sunset": "Every sunset" }, "user": { - "back-home": "User left home", - "left-home": "User back at home" + "back-home": "User back at home", + "left-home": "User left home" }, "house": { "empty": "House is empty", @@ -2175,7 +2186,8 @@ "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)" + "onlyExecuteAtThreshold": "Execute only when threshold is passed (and not at every value sent by the device)", + "activateOrDeactivateForDuration": "Run the scene after the condition has been valid for:" }, "scheduledTrigger": { "typeLabel": "Type", @@ -2929,7 +2941,15 @@ "48": "Long press", "49": "Short release", "50": "Long release", - "51": "Double press" + "51": "Double press", + "52": "Toggle", + "53": "Toggle Hold", + "54": "Brightness Up Click", + "55": "Brightness Up Hold", + "56": "Brightness Up Release", + "57": "Brightness Down Click", + "58": "Brightness Down Hold", + "59": "Brightness Down Release" } }, "heater": { @@ -3095,7 +3115,8 @@ }, "button": { "shortCategoryName": "Button", - "click": "Button (click)" + "click": "Button (click)", + "push": "Push button" }, "pressure-sensor": { "shortCategoryName": "Pressure sensor", @@ -3199,6 +3220,67 @@ "index": "Index", "daily-consumption": "Daily Consumption" }, + "teleinformation": { + "shortCategoryName": "Teleinformation", + "binary": "Switch", + "east": "Total active power delivered (EAST)", + "eait": "Total active power injected (EAIT)", + "easf01": "Total provider active power delivered (EASF01)", + "easf02": "Total provider active power delivered (EASF02)", + "easf03": "Total provider active power delivered (EASF03)", + "easf04": "Total provider active power delivered (EASF04)", + "easf05": "Total provider active power delivered (EASF05)", + "easf06": "Total provider active power delivered (EASF06)", + "easf07": "Total provider active power delivered (EASF07)", + "easf08": "Total provider active power delivered (EASF08)", + "easf09": "Total provider active power delivered (EASF09)", + "easf10": "Total provider active power delivered (EASF10)", + "pref": "Apparent power of reference (PREF)", + "pcoup": "Apparent power threshold (PCOUP)", + "vtic": "Protocol version (VTIC)", + "ccasn": "Current point of the active load curve drawn (CCASN)", + "ccasn_1": "Previous point of the active load curve drawn (CCASN-1)", + "umoy1": "Average RMS voltage (UMOY1)", + "umoy2": "Average RMS voltage (UMOY2)", + "umoy3": "Average RMS voltage (UMOY3)", + "erq1": "Total reactive power Q1 (ERQ1)", + "erq2": "Total reactive power Q2 (ERQ2)", + "erq3": "Total reactive power Q3 (ERQ3)", + "erq4": "Total reactive power Q4 (ERQ4)", + "irms1": "RMS current (IRMS1)", + "irms2": "RMS current (IRMS2)", + "irms3": "RMS current (IRMS3)", + "urms1": "RMS voltage (URMS1)", + "urms2": "RMS voltage (URMS2)", + "urms3": "RMS voltage (URMS3)", + "easd01": "Active energy withdrawn Distributor (EASD01)", + "easd02": "Active energy withdrawn Distributor (EASD02)", + "easd03": "Active energy withdrawn Distributor (EASD03)", + "easd04": "Active energy withdrawn Distributor (EASD04)", + "ntarf": "Tarif index number (NTARF)", + "ccain": "Point n of the withdrawn active load curve (CCAIN)", + "ccain_1": "Point n-1 of the withdrawn active load curve (CCAIN-1)", + "sinsti": "Instantaneous apparent power injected (SINSTI)", + "smaxin": "Apparent power max. injected n (SMAXIN)", + "smaxin_1": "Apparent power max. injected n-1 (SMAXIN-1)", + "smaxn": "Apparent power delivered peak (SMAXN)", + "smaxn2": "Apparent power delivered peak (SMAXN2)", + "smaxn3": "Apparent power delivered peak (SMAXN3)", + "sinsts": "Immediate apparent power delivered (SINSTS)", + "sinsts2": "Immediate apparent power delivered (SINSTS2)", + "sinsts3": "Immediate apparent power delivered (SINSTS3)", + "smaxn_1": "Apparent power max. draw-off n-1 (SMAXN-1)", + "smaxn2_1": "Apparent power max. draw-off n-1 (SMAXN2-1)", + "smaxn3_1": "Apparent power max. draw-off n-1 (SMAXN3-1)", + "hhphc": "HPHC Program (HHPHC)", + "imax": "RMS current peak (IMAX)", + "adps": "Subscribed Power Exceeded Warning (ADPS)", + "imax2": "RMS current peak (IMAX2)", + "imax3": "RMS current peak (IMAX3)", + "adir1": "Overcurrent alert (ADIR1)", + "adir2": "Overcurrent alert (ADIR2)", + "adir3": "Overcurrent alert (ADIR3)" + }, "volume-sensor": { "shortCategoryName": "Volume sensor", "decimal": "Volume (decimal)", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 62a3ca5bd2..0689ae59b8 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -298,7 +298,8 @@ "deviceTitle": "{{name}} - {{type}}", "addButton": "+", "substractButton": "-", - "motionDetected": "Mouvement détecté" + "motionDetected": "Mouvement détecté", + "pushButton": "Appuyer" }, "devices": { "editDeviceFeaturesLabel": "Vous pouvez modifier le nom affiché ici :", @@ -2035,6 +2036,13 @@ "variablesExplanation": "Pour injecter une variable, tapez '{{ '. Attention, vous devez avoir défini une variable auparavant dans une action 'Récupérer le dernier état' placé avant ce bloc message.", "messagePlaceholder": "Mon message" }, + "zigbee2mqttMessage": { + "topic": "Topic", + "topicPlaceholder": "/gladys/my-topic", + "messageLabel": "Message", + "variablesExplanation": "Pour injecter une variable, tapez '{{ '. Attention, vous devez avoir défini une variable auparavant dans une action 'Récupérer le dernier état' placé avant ce bloc message.", + "messagePlaceholder": "Mon message" + }, "playNotification": { "description": "Cette action fera parler Gladys sur l'enceinte sélectionnée.", "needGladysPlus": "Nécessite Gladys Plus car les API de \"Text To Speech\" sont payantes.", @@ -2103,6 +2111,9 @@ "mqtt": { "send": "Envoyer un message MQTT" }, + "zigbee2mqtt": { + "send": "Envoyer un message Zigbee2mqtt" + }, "music": { "play-notification": "Parler sur une enceinte" }, @@ -2175,7 +2186,8 @@ "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 )" + "onlyExecuteAtThreshold": "Exécuter seulement lorsque le seuil est passé ( et non pas à chaque valeur envoyée )", + "activateOrDeactivateForDuration": "Exécuter la scène après que la condition ait été valide pendant : " }, "scheduledTrigger": { "typeLabel": "Type", @@ -2929,7 +2941,15 @@ "48": "Pression longue", "49": "Relâchement court", "50": "Relâchement long", - "51": "Pression double" + "51": "Pression double", + "52": "Bouton Bascule", + "53": "Bouton Bascule maintenu", + "54": "Luminosité Plus Clic", + "55": "Luminosité Plus Maintenu", + "56": "Luminosité Plus Relâchée", + "57": "Luminosité Moins Clic", + "58": "Luminosité Moins Maintenu", + "59": "Luminosité Moins Relâchée" } }, "heater": { @@ -3095,7 +3115,8 @@ }, "button": { "shortCategoryName": "Bouton", - "click": "Clic bouton" + "click": "Clic bouton", + "push": "Bouton poussoir" }, "pressure-sensor": { "shortCategoryName": "Capteur de pression", @@ -3199,6 +3220,67 @@ "index": "Index", "daily-consumption": "Consommation journalière" }, + "teleinformation": { + "shortCategoryName": "Téléinformation", + "binary": "Relais", + "east": "Energie active soutirée totale (EAST)", + "eait": "Energie active injectée totale (EAIT)", + "easf01": "Energie active soutirée fournisseur (EASF01)", + "easf02": "Energie active soutirée fournisseur (EASF02)", + "easf03": "Energie active soutirée fournisseur (EASF03)", + "easf04": "Energie active soutirée fournisseur (EASF04)", + "easf05": "Energie active soutirée fournisseur (EASF05)", + "easf06": "Energie active soutirée fournisseur (EASF06)", + "easf07": "Energie active soutirée fournisseur (EASF07)", + "easf08": "Energie active soutirée fournisseur (EASF08)", + "easf09": "Energie active soutirée fournisseur (EASF09)", + "easf10": "Energie active soutirée fournisseur (EASF10)", + "pref": "Puissance apparente référence (PREF)", + "pcoup": "Puissance apparente coupure (PCOUP)", + "vtic": "Version du protocole (VTIC)", + "ccasn": "Position de la courbe charge active (CCASN)", + "ccasn_1": "Position de la courbe charge active (CCASN-1)", + "umoy1": "Tension moyenne (UMOY1)", + "umoy2": "Tension moyenne (UMOY2)", + "umoy3": "Tension moyenne (UMOY3)", + "erq1": "Energie réactive Q1 totale (ERQ1)", + "erq2": "Energie réactive Q2 totale (ERQ2)", + "erq3": "Energie réactive Q3 totale (ERQ3)", + "erq4": "Energie réactive Q4 totale (ERQ4)", + "irms1": "Courant efficace (IRMS1)", + "irms2": "Courant efficace (IRMS2)", + "irms3": "Courant efficace (IRMS3)", + "urms1": "Tension efficace (URMS1)", + "urms2": "Tension efficace (URMS2)", + "urms3": "Tension efficace (URMS3)", + "easd01": "Energie active soutirée distributeur (EASD01)", + "easd02": "Energie active soutirée distributeur (EASD02)", + "easd03": "Energie active soutirée distributeur (EASD03)", + "easd04": "Energie active soutirée distributeur (EASD04)", + "ntarf": "Numéro d'indice tarifaire (NTARF)", + "ccain": "Point n de la courbe de charge active injectée (CCAIN)", + "ccain_1": "Point n-1 de la courbe de charge active injectée (CCAIN-1)", + "sinsti": "Puissance apparente instantanée injectée (SINSTI)", + "smaxin": "Puissance apparente max. injectée n (SMAXIN)", + "smaxin_1": "Puissance apparente max. injectée n-1 (SMAXIN-1)", + "smaxn": "Puissance apparente max. soutirée (SMAXN)", + "smaxn2": "Puissance apparente max. soutirée (SMAXN2)", + "smaxn3": "Puissance apparente max. soutirée (SMAXN3)", + "sinsts": "Puissance apparente instantanée soutirée (SINSTS)", + "sinsts2": "Puissance apparente instantanée soutirée (SINSTS2)", + "sinsts3": "Puissance apparente instantanée soutirée (SINSTS3)", + "smaxn_1": "Puissance apparente max. soutirée n-1 (SMAXN-1)", + "smaxn2_1": "Puissance apparente max. soutirée n-1 (SMAXN2-1)", + "smaxn3_1": "Puissance apparente max. soutirée n-1 (SMAXN3-1)", + "hhphc": "Programme HPHC (HHPHC)", + "imax": "Intensité maximale (IMAX)", + "adps": "Alerte dépassement puissance souscrite (ADPS)", + "imax2": "Intensité maximale (IMAX2)", + "imax3": "Intensité maximale (IMAX3)", + "adir1": "Alerte de surcourant (ADIR1)", + "adir2": "Alerte de surcourant (ADIR2)", + "adir3": "Alerte de surcourant (ADIR3)" + }, "volume-sensor": { "shortCategoryName": "Capteur de volume", "decimal": "Volume (décimale)", diff --git a/front/src/routes/integration/all/mqtt/device-page/setup/index.js b/front/src/routes/integration/all/mqtt/device-page/setup/index.js index 04be129bef..1b605644bd 100644 --- a/front/src/routes/integration/all/mqtt/device-page/setup/index.js +++ b/front/src/routes/integration/all/mqtt/device-page/setup/index.js @@ -43,6 +43,12 @@ class MqttDeviceSetupPage extends Component { defaultValues.keep_history = false; } + if (featureData[1] === DEVICE_FEATURE_TYPES.BUTTON.PUSH) { + defaultValues.min = 1; + defaultValues.max = 1; + defaultValues.read_only = false; + } + const device = update(this.state.device, { features: { $push: [ diff --git a/front/src/routes/scene/edit-scene/ActionCard.jsx b/front/src/routes/scene/edit-scene/ActionCard.jsx index 7fae038dba..e7cfb314b4 100644 --- a/front/src/routes/scene/edit-scene/ActionCard.jsx +++ b/front/src/routes/scene/edit-scene/ActionCard.jsx @@ -29,6 +29,7 @@ import SendMessageCameraParams from './actions/SendMessageCameraParams'; import CheckAlarmMode from './actions/CheckAlarmMode'; import SetAlarmMode from './actions/SetAlarmMode'; import SendMqttMessage from './actions/SendMqttMessage'; +import SendZigbee2MqttMessage from './actions/SendZigbee2MqttMessage'; import PlayNotification from './actions/PlayNotification'; import EdfTempoCondition from './actions/EdfTempoCondition'; import AskAI from './actions/AskAI'; @@ -66,6 +67,7 @@ const ACTION_ICON = { [ACTIONS.ALARM.SET_ALARM_MODE]: 'fe fe-bell', [ACTIONS.MQTT.SEND]: 'fe fe-message-square', [ACTIONS.MUSIC.PLAY_NOTIFICATION]: 'fe fe-speaker', + [ACTIONS.ZIGBEE2MQTT.SEND]: 'fe fe-message-square', [ACTIONS.AI.ASK]: 'fe fe-cpu' }; @@ -104,6 +106,7 @@ const ActionCard = ({ children, ...props }) => { props.action.type === ACTIONS.MESSAGE.SEND || props.action.type === ACTIONS.CALENDAR.IS_EVENT_RUNNING || props.action.type === ACTIONS.MQTT.SEND || + props.action.type === ACTIONS.ZIGBEE2MQTT.SEND || props.action.type === ACTIONS.LIGHT.BLINK, 'col-lg-4': props.action.type !== ACTIONS.CONDITION.ONLY_CONTINUE_IF && @@ -392,6 +395,17 @@ const ActionCard = ({ children, ...props }) => { triggersVariables={props.triggersVariables} /> )} + {props.action.type === ACTIONS.ZIGBEE2MQTT.SEND && ( + + )} {props.action.type === ACTIONS.MUSIC.PLAY_NOTIFICATION && ( { + this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'topic', e.target.value); + }; + handleChangeMessage = text => { + const newMessage = text && text.length > 0 ? text : undefined; + this.props.updateActionProperty(this.props.columnIndex, this.props.index, 'message', newMessage); + }; + + render(props) { + return ( +
+
+
+ + + } + /> + +
+
+ +
+ +
+ + } + /> + +
+
+
+ ); + } +} + +export default connect('httpClient', {})(SendZigbee2MqttMessage); diff --git a/front/src/routes/scene/edit-scene/triggers/DeviceFeatureState.jsx b/front/src/routes/scene/edit-scene/triggers/DeviceFeatureState.jsx index 2cac11f1c8..3e6474bdd1 100644 --- a/front/src/routes/scene/edit-scene/triggers/DeviceFeatureState.jsx +++ b/front/src/routes/scene/edit-scene/triggers/DeviceFeatureState.jsx @@ -1,5 +1,6 @@ import { Component } from 'preact'; import { connect } from 'unistore/preact'; +import { Text, Localizer } from 'preact-i18n'; import { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } from '../../../../../../server/utils/constants'; @@ -24,6 +25,24 @@ class TurnOnLight extends Component { } }; + onForDurationChange = e => { + e.preventDefault(); + if (e.target.value) { + this.props.updateTriggerProperty(this.props.index, 'for_duration', Number(e.target.value) * 60 * 1000); + } else { + this.props.updateTriggerProperty(this.props.index, 'for_duration', ''); + } + }; + + enableOrDisableForDuration = e => { + e.preventDefault(); + if (e.target.checked) { + this.props.updateTriggerProperty(this.props.index, 'for_duration', 60 * 1000); + } else { + this.props.updateTriggerProperty(this.props.index, 'for_duration', undefined); + } + }; + render(props, { selectedDeviceFeature }) { let binaryDevice = false; let presenceDevice = false; @@ -55,12 +74,55 @@ class TurnOnLight extends Component { {binaryDevice && } - {presenceDevice && } + {presenceDevice && } {buttonClickDevice && } {pilotWireModeDevice && } {defaultDevice && } {thresholdDevice && } +
+
+ +
+
+ {props.trigger.for_duration !== undefined && ( +
+
+
+
+ + } + value={ + Number.isInteger(props.trigger.for_duration) + ? props.trigger.for_duration / 60 / 1000 + : props.trigger.for_duration + } + onChange={this.onForDurationChange} + /> + + + + + + +
+
+
+
+ )} ); } diff --git a/front/src/routes/scene/edit-scene/triggers/device-states/PresenceSensorDeviceState.jsx b/front/src/routes/scene/edit-scene/triggers/device-states/PresenceSensorDeviceState.jsx index e1a4d63307..8e086069f2 100644 --- a/front/src/routes/scene/edit-scene/triggers/device-states/PresenceSensorDeviceState.jsx +++ b/front/src/routes/scene/edit-scene/triggers/device-states/PresenceSensorDeviceState.jsx @@ -8,6 +8,18 @@ class PresenceSensorDeviceState extends Component { this.props.updateTriggerProperty(this.props.index, 'threshold_only', false); } + componentDidUpdate(prevProps) { + if ( + prevProps.selectedDeviceFeature && + this.props.selectedDeviceFeature && + prevProps.selectedDeviceFeature.selector !== this.props.selectedDeviceFeature.selector + ) { + this.props.updateTriggerProperty(this.props.index, 'operator', '='); + this.props.updateTriggerProperty(this.props.index, 'value', 1); + this.props.updateTriggerProperty(this.props.index, 'threshold_only', false); + } + } + render() { return (
diff --git a/front/src/utils/consts.js b/front/src/utils/consts.js index 3df6aa1de0..aa0206e016 100644 --- a/front/src/utils/consts.js +++ b/front/src/utils/consts.js @@ -270,7 +270,8 @@ export const DeviceFeatureCategoriesIcon = { [DEVICE_FEATURE_TYPES.CUBE.ROTATION]: 'rotate-cw' }, [DEVICE_FEATURE_CATEGORIES.BUTTON]: { - [DEVICE_FEATURE_TYPES.BUTTON.CLICK]: 'circle' + [DEVICE_FEATURE_TYPES.BUTTON.CLICK]: 'circle', + [DEVICE_FEATURE_TYPES.BUTTON.PUSH]: 'circle' }, [DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR]: { [DEVICE_FEATURE_TYPES.ENERGY_SENSOR.BINARY]: 'power', @@ -280,6 +281,66 @@ export const DeviceFeatureCategoriesIcon = { [DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE]: 'zap', [DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX]: 'zap' }, + [DEVICE_FEATURE_CATEGORIES.TELEINFORMATION]: { + [DEVICE_FEATURE_TYPES.TELEINFORMATION.BINARY]: 'power', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EAST]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EAIT]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF01]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF02]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF03]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF04]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF05]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF06]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF07]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF08]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF09]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF10]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.PREF]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.PCOUP]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.VTIC]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.CCASN]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.CCASN_1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ4]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD01]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD02]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD03]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD04]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.NTARF]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.CCAIN]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.CCAIN_1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTI]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXIN]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXIN_1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN_1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN2_1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN3_1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.HHPHC]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ADPS]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX3]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ADIR1]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ADIR2]: 'zap', + [DEVICE_FEATURE_TYPES.TELEINFORMATION.ADIR3]: 'zap' + }, [DEVICE_FEATURE_CATEGORIES.SPEED_SENSOR]: { [DEVICE_FEATURE_TYPES.SPEED_SENSOR.DECIMAL]: 'wind', [DEVICE_FEATURE_TYPES.SPEED_SENSOR.INTEGER]: 'wind' diff --git a/package-lock.json b/package-lock.json index 3928d5c03b..222850ba32 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gladys", - "version": "4.48.0", + "version": "4.49.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gladys", - "version": "4.48.0", + "version": "4.49.0", "hasInstallScript": true, "license": "Apache-2.0", "devDependencies": { diff --git a/package.json b/package.json index 0968fde836..2fa1348e02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gladys", - "version": "4.48.0", + "version": "4.49.0", "description": "A privacy-first, open-source home assistant", "main": "index.js", "engines": { diff --git a/server/lib/gateway/gateway.forwardMessageToOpenAI.js b/server/lib/gateway/gateway.forwardMessageToOpenAI.js index bcdae2a0f9..29e84052c3 100644 --- a/server/lib/gateway/gateway.forwardMessageToOpenAI.js +++ b/server/lib/gateway/gateway.forwardMessageToOpenAI.js @@ -11,7 +11,7 @@ const intentTranslation = { INFO: 'info.get-info', }; -const disableOpenAiFirstReply = new Set(['GET_TEMPERATURE', 'GET_HUMIDITY']); +const disableOpenAiFirstReply = new Set(['GET_TEMPERATURE', 'GET_HUMIDITY', 'NO_RESPONSE']); /** * @public diff --git a/server/lib/scene/index.js b/server/lib/scene/index.js index 1e1f26298d..eb141e567c 100644 --- a/server/lib/scene/index.js +++ b/server/lib/scene/index.js @@ -57,6 +57,7 @@ const SceneManager = function SceneManager( this.sunCalc = sunCalc; this.scheduler = scheduler; this.jobs = []; + this.checkTriggersDurationTimer = new Map(); this.event.on(EVENTS.TRIGGERS.CHECK, eventFunctionWrapper(this.checkTrigger.bind(this))); this.event.on(EVENTS.ACTION.TRIGGERED, eventFunctionWrapper(this.executeSingleAction.bind(this))); // on timezone change, reload all scenes diff --git a/server/lib/scene/scene.actions.js b/server/lib/scene/scene.actions.js index dafbcc422e..99df9acc72 100644 --- a/server/lib/scene/scene.actions.js +++ b/server/lib/scene/scene.actions.js @@ -565,6 +565,14 @@ const actionsFunc = { mqttService.device.publish(action.topic, messageWithVariables); } }, + [ACTIONS.ZIGBEE2MQTT.SEND]: (self, action, scope) => { + const zigbee2mqttService = self.service.getService('zigbee2mqtt'); + + if (zigbee2mqttService) { + const messageWithVariables = Handlebars.compile(action.message)(scope); + zigbee2mqttService.device.publish(action.topic, messageWithVariables); + } + }, [ACTIONS.MUSIC.PLAY_NOTIFICATION]: async (self, action, scope) => { // Get device const device = self.stateManager.get('device', action.device); diff --git a/server/lib/scene/scene.checkTrigger.js b/server/lib/scene/scene.checkTrigger.js index 8143e760e8..3d93c25b49 100644 --- a/server/lib/scene/scene.checkTrigger.js +++ b/server/lib/scene/scene.checkTrigger.js @@ -30,7 +30,7 @@ function checkTrigger(event) { if (event.type === trigger.type) { logger.debug(`Trigger ${trigger.type} is matching with event`); // then we check the condition is verified - const conditionVerified = triggersFunc[event.type](event, trigger); + const conditionVerified = triggersFunc[event.type](this, sceneSelector, event, trigger); logger.debug(`Trigger ${trigger.type}, conditionVerified = ${conditionVerified}...`); // if yes, we execute the scene diff --git a/server/lib/scene/scene.triggers.js b/server/lib/scene/scene.triggers.js index 6761088484..a557459d61 100644 --- a/server/lib/scene/scene.triggers.js +++ b/server/lib/scene/scene.triggers.js @@ -1,38 +1,106 @@ +const cloneDeep = require('lodash.clonedeep'); + +const logger = require('../../utils/logger'); const { EVENTS } = require('../../utils/constants'); const { compare } = require('../../utils/compare'); const triggersFunc = { - [EVENTS.DEVICE.NEW_STATE]: (event, trigger) => { - // we check that we are talking about the same event + [EVENTS.DEVICE.NEW_STATE]: (self, sceneSelector, event, trigger) => { + // we check that we are talking about the same device feature if (event.device_feature !== trigger.device_feature) { return false; } + + // We verify if both old value and new value validate the rule const newValueValidateRule = compare(trigger.operator, event.last_value, trigger.value); - // if the trigger is only a threshold_only, we only validate the trigger is the rule has been validated - // and was not validated with the previous value - if (trigger.threshold_only === true && !Number.isNaN(event.previous_value)) { - const previousValueValidateRule = compare(trigger.operator, event.previous_value, trigger.value); - return newValueValidateRule && !previousValueValidateRule; + const previousValueValidateRule = compare(trigger.operator, event.previous_value, trigger.value); + + const triggerDurationKey = `device.new-state.${sceneSelector}.${trigger.device_feature}:${trigger.operator}:${trigger.value}`; + + // If the previous value was validating the rule, and the new value is not + // We clear any timeout for this trigger + if (previousValueValidateRule && !newValueValidateRule && self.checkTriggersDurationTimer.get(triggerDurationKey)) { + logger.info( + `Cancelling timer on trigger for device_feature ${trigger.device_feature}, because condition is no longer valid`, + ); + clearTimeout(self.checkTriggersDurationTimer.get(triggerDurationKey)); + self.checkTriggersDurationTimer.delete(triggerDurationKey); + } + + if (trigger.for_duration === undefined) { + // If the trigger is only a threshold_only, we only validate the trigger is the rule has been validated + // and was not validated with the previous value + if (trigger.threshold_only === true && !Number.isNaN(event.previous_value)) { + return newValueValidateRule && !previousValueValidateRule; + } + + return newValueValidateRule; } - return newValueValidateRule; + + // If the "for_duration_finished" is here, it means we are + // checking the state after the timeout + if (event.for_duration_finished && triggerDurationKey === event.trigger_duration_key) { + logger.info(`Scene trigger device.new-state: Timer for sensor ${trigger.device_feature} has finished.`); + clearTimeout(self.checkTriggersDurationTimer.get(triggerDurationKey)); + self.checkTriggersDurationTimer.delete(triggerDurationKey); + return newValueValidateRule; + } + + const isValidatedIfThresholdOnly = + trigger.threshold_only && !Number.isNaN(event.previous_value) + ? newValueValidateRule && !previousValueValidateRule + : true; + + if (newValueValidateRule && isValidatedIfThresholdOnly) { + // If the timeout already exist, don't re-create it + const timeoutAlreadyExist = self.checkTriggersDurationTimer.get(triggerDurationKey); + if (timeoutAlreadyExist) { + logger.info(`Timer for "${trigger.device_feature}" already exist, not re-creating.`); + return false; + } + logger.info( + `Scheduling timer to check for device_feature "${trigger.device_feature}" state in ${trigger.for_duration}ms`, + ); + // Create a timeout + const timeoutId = setTimeout(() => { + const lastValue = self.stateManager.get('deviceFeature', trigger.device_feature).last_value; + self.event.emit(EVENTS.TRIGGERS.CHECK, { + ...cloneDeep(event), + previous_value: event.last_value, + last_value: lastValue, + for_duration_finished: true, + trigger_duration_key: triggerDurationKey, + }); + }, trigger.for_duration); + // Save the timeoutId in case we need to cancel it later + self.checkTriggersDurationTimer.set(triggerDurationKey, timeoutId); + // Return false, as we'll check this only in the future + return false; + } + + return false; }, - [EVENTS.TIME.CHANGED]: (event, trigger) => event.key === trigger.key, - [EVENTS.TIME.SUNRISE]: (event, trigger) => event.house.selector === trigger.house, - [EVENTS.TIME.SUNSET]: (event, trigger) => event.house.selector === trigger.house, - [EVENTS.USER_PRESENCE.BACK_HOME]: (event, trigger) => event.house === trigger.house && event.user === trigger.user, - [EVENTS.USER_PRESENCE.LEFT_HOME]: (event, trigger) => event.house === trigger.house && event.user === trigger.user, - [EVENTS.HOUSE.EMPTY]: (event, trigger) => event.house === trigger.house, - [EVENTS.HOUSE.NO_LONGER_EMPTY]: (event, trigger) => event.house === trigger.house, - [EVENTS.AREA.USER_ENTERED]: (event, trigger) => event.user === trigger.user && event.area === trigger.area, - [EVENTS.AREA.USER_LEFT]: (event, trigger) => event.user === trigger.user && event.area === trigger.area, - [EVENTS.ALARM.ARM]: (event, trigger) => event.house === trigger.house, - [EVENTS.ALARM.ARMING]: (event, trigger) => event.house === trigger.house, - [EVENTS.ALARM.DISARM]: (event, trigger) => event.house === trigger.house, - [EVENTS.ALARM.PARTIAL_ARM]: (event, trigger) => event.house === trigger.house, - [EVENTS.ALARM.PANIC]: (event, trigger) => event.house === trigger.house, - [EVENTS.ALARM.TOO_MANY_CODES_TESTS]: (event, trigger) => event.house === trigger.house, + [EVENTS.TIME.CHANGED]: (self, sceneSelector, event, trigger) => event.key === trigger.key, + [EVENTS.TIME.SUNRISE]: (self, sceneSelector, event, trigger) => event.house.selector === trigger.house, + [EVENTS.TIME.SUNSET]: (self, sceneSelector, event, trigger) => event.house.selector === trigger.house, + [EVENTS.USER_PRESENCE.BACK_HOME]: (self, sceneSelector, event, trigger) => + event.house === trigger.house && event.user === trigger.user, + [EVENTS.USER_PRESENCE.LEFT_HOME]: (self, sceneSelector, event, trigger) => + event.house === trigger.house && event.user === trigger.user, + [EVENTS.HOUSE.EMPTY]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.HOUSE.NO_LONGER_EMPTY]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.AREA.USER_ENTERED]: (self, sceneSelector, event, trigger) => + event.user === trigger.user && event.area === trigger.area, + [EVENTS.AREA.USER_LEFT]: (self, sceneSelector, event, trigger) => + event.user === trigger.user && event.area === trigger.area, + [EVENTS.ALARM.ARM]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.ALARM.ARMING]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.ALARM.DISARM]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.ALARM.PARTIAL_ARM]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.ALARM.PANIC]: (self, sceneSelector, event, trigger) => event.house === trigger.house, + [EVENTS.ALARM.TOO_MANY_CODES_TESTS]: (self, sceneSelector, event, trigger) => event.house === trigger.house, [EVENTS.SYSTEM.START]: () => true, - [EVENTS.MQTT.RECEIVED]: (event, trigger) => + [EVENTS.MQTT.RECEIVED]: (self, sceneSelector, event, trigger) => event.topic === trigger.topic && (trigger.message === '' || trigger.message === event.message), }; diff --git a/server/lib/session/session.get.js b/server/lib/session/session.get.js index 90fe07e1eb..13307cbbba 100644 --- a/server/lib/session/session.get.js +++ b/server/lib/session/session.get.js @@ -37,6 +37,7 @@ async function get(userId, options) { order: [[optionsWithDefault.order_by, optionsWithDefault.order_dir]], where: { revoked: false, + user_id: userId, }, }); diff --git a/server/lib/user/user.get.js b/server/lib/user/user.get.js index e99cb844d7..7e1aa0bcab 100644 --- a/server/lib/user/user.get.js +++ b/server/lib/user/user.get.js @@ -65,6 +65,8 @@ async function get(options) { if (userPlain.picture && userPlain.picture.toString) { userPlain.picture = userPlain.picture.toString('utf8'); } + delete userPlain.password; + delete userPlain.telegram_user_id; return userPlain; }); diff --git a/server/migrations/20241105200700-update-lixee-tic-features.js b/server/migrations/20241105200700-update-lixee-tic-features.js new file mode 100644 index 0000000000..4b24fc077a --- /dev/null +++ b/server/migrations/20241105200700-update-lixee-tic-features.js @@ -0,0 +1,895 @@ +const Promise = require('bluebird'); +const db = require('../models'); +const logger = require('../utils/logger'); +const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../utils/constants'); + +module.exports = { + up: async (queryInterface, Sequelize) => { + // Get Zigbee2mqtt service + const service = await db.Service.findOne({ + where: { + name: 'zigbee2mqtt', + }, + }); + if (service === null) { + logger.info('Zigbee2mqtt service not found.'); + return; + } + logger.info(`Zigbee2mqtt migration: Found service zigbee2mqtt = ${service.id}`); + const zigbee2mqttDevices = await db.Device.findAll({ + where: { + service_id: service.id, + }, + }); + logger.info(`Zigbee2mqtt migration: Found ${zigbee2mqttDevices.length} zigbee2mqtt devices`); + + const lixeeTicdevices = await db.Device.findAll({ + where: { + model: `ZLinky_TIC`, + }, + }); + logger.info(`Zigbee2mqtt migration: Found ${lixeeTicdevices.length} lixee-tic devices`); + + await Promise.each(lixeeTicdevices, async (lixeeTicdevice) => { + // Load impacted features + const features = await db.DeviceFeature.findAll({ + where: { + device_id: lixeeTicdevice.id, + }, + }); + logger.info(`Zigbee2mqtt migration: Found ${features.length} Linky_TIC features`); + + await Promise.mapSeries(features, async (feature) => { + const { id, selector, name } = feature; + + // Modify EAST + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EAST, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active délivrée totale (EAST)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EAIT + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-summ-received` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EAIT, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active injectée totale (EAIT)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF01 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier1-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF01, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF01)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF02 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier2-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF02, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF02)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF03 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier3-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF03, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF03)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF04 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier4-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF04, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF04)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF05 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier5-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF05, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF05)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF06 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier6-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF06, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF06)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF07 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier7-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF07, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF07)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF08 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier8-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF08, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF08)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF09 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier9-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF09, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF09)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASF10 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-tier10-summ-delivered` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF10, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée fournisseur (EASF10)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify PREF + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT}-available-power` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.PREF, + }; + + if (name === 'Intensité') { + currentFields.name = 'Puissance apparente référence (PREF)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify PCOUP + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT}-power-threshold` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.PCOUP, + }; + + if (name === 'Intensité') { + currentFields.name = 'Puissance apparente coupure (PCOUP)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify CCASN + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-active-power` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCASN, + }; + + if (name === 'Puissance') { + currentFields.name = 'Position de la courbe charge active (CCASN)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify CCASN-1 Position de la courbe charge active (CCASN-1) + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-active-power-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCASN_1, + }; + + if (name === 'Puissance') { + currentFields.name = 'Position de la courbe charge active (CCASN-1)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify UMOY1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE}-average-rms-voltage-meas-period` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY1, + }; + + if (name === 'Tension') { + currentFields.name = 'Tension moyenne (UMOY1)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify UMOY2 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE}-average-rms-voltage-measure-period-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY2, + }; + + if (name === 'Tension') { + currentFields.name = 'Tension moyenne (UMOY2)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify UMOY3 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE}-average-rms-voltage-meas-period-ph-c` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY3, + }; + + if (name === 'Tension') { + currentFields.name = 'Tension moyenne (UMOY3)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify ERQ1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-total-reactive-power` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ1, + }; + + if (name === 'Puissance') { + currentFields.name = 'Puissance réactive Q1 totale (ERQ1)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify ERQ2 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-reactive-power` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ2, + }; + + if (name === 'Puissance') { + currentFields.name = 'Puissance réactive Q2 totale (ERQ2)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify ERQ3 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-reactive-power-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ3, + }; + + if (name === 'Puissance') { + currentFields.name = 'Puissance réactive Q3 totale (ERQ3)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify ERQ4 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-reactive-power-ph-c` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ4, + }; + + if (name === 'Puissance') { + currentFields.name = 'Puissance réactive Q4 totale (ERQ4)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify IRMS1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT}-rms-current` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS1, + }; + + if (name === 'Intensité') { + currentFields.name = 'Courant efficace (IRMS1)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify URMS1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE}-rms-voltage` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS1, + }; + + if (name === 'Tension') { + currentFields.name = 'Tension efficace (URMS1)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify URMS2 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE}-rms-voltage-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS2, + }; + + if (name === 'Tension') { + currentFields.name = 'Tension efficace (URMS2)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify URMS3 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE}-rms-voltage-ph-c` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS3, + }; + + if (name === 'Tension') { + currentFields.name = 'Tension efficace (URMS3)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASD01 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-active-energy-out-d01` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD01, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée distributeur (EASD01)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASD02 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-active-energy-out-d02` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD02, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée distributeur (EASD02)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASD03 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-active-energy-out-d03` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD03, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée distributeur (EASD03)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify EASD04 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-active-energy-out-d04` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD04, + }; + + if (name === 'Index') { + currentFields.name = 'Energie active soutirée distributeur (EASD04)'; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify NTARF + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX}-current-index-tarif` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.NTARF, + }; + + if (name === 'Index') { + currentFields.name = `Numéro d'indice tarifaire (NTARF)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify CCAIN + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-injected-active-load-n` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCAIN, + }; + + if (name === 'Puissance') { + currentFields.name = `Point n de la courbe de charge active retirée (CCAIN)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify CCAIN-1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-injected-active-load-n1` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCAIN_1, + }; + + if (name === 'Puissance') { + currentFields.name = `Point n-1 de la courbe de charge active retirée (CCAIN-1)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SINSTI + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-injected-v-a` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTI, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente instantanée injectée (SINSTI)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXIN + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-injected-v-a-max-n` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXIN, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. injectée n (SMAXIN)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXIN-1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-injected-v-a-max-n1` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXIN_1, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. injectée n-1 (SMAXIN-1)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify RELAIS + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.BINARY}-relais` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.BINARY, + }; + + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXN + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-active-power-max` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. soutirée (SMAXN)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXN2 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-active-power-max-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN2, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. soutirée (SMAXN2)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXN3 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-active-power-max-ph-c` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN3, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. soutirée (SMAXN3)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SINSTS + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-apparent-power` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente instantanée soutirée (SINSTS)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SINSTS2 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-apparent-power-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS2, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente instantanée soutirée (SINSTS2)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SINSTS3 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-apparent-power-ph-c` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS3, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente instantanée soutirée (SINSTS3)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXN-1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-drawn-v-a-max-n1` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN_1, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. soutirée n-1 (SMAXN-1)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXN2-1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-drawn-v-a-max-n1-p2` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN2_1, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. soutirée n-1 (SMAXN2-1)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify SMAXN3-1 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER}-drawn-v-a-max-n1-p3` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN3_1, + }; + + if (name === 'Puissance') { + currentFields.name = `Puissance apparente max. soutirée n-1 (SMAXN3-1)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify IMAX + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT}-rms-current-max` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX, + }; + + if (name === 'Intensité') { + currentFields.name = `Intensité maximale (IMAX)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify IMAX2 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT}-rms-current-max-ph-b` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX2, + }; + + if (name === 'Intensité') { + currentFields.name = `Intensité maximale (IMAX2)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + + // Modify IMAX3 + if ( + selector === + `${service.selector}-${lixeeTicdevice.selector}-${DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR}-${DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT}-rms-current-max-ph-c` + ) { + const currentFields = { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX3, + }; + + if (name === 'Intensité') { + currentFields.name = `Intensité maximale (IMAX3)`; + } + await feature.update(currentFields); + logger.info(`Zigbee2mqtt migration: Updating device_feature ${id}`); + } + }); + }); + }, + down: async (queryInterface, Sequelize) => {}, +}; diff --git a/server/models/scene.js b/server/models/scene.js index 8143ab3097..7c91a90c7b 100644 --- a/server/models/scene.js +++ b/server/models/scene.js @@ -99,6 +99,7 @@ const triggersSchema = Joi.array().items( time: Joi.string().regex(/^([0-9]{2}):([0-9]{2})$/), interval: Joi.number(), unit: Joi.string(), + for_duration: Joi.number(), days_of_the_week: Joi.array().items( Joi.string().valid('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'), ), diff --git a/server/services/zigbee2mqtt/exposes/enumType.js b/server/services/zigbee2mqtt/exposes/enumType.js index 8193ad5bc0..d133356904 100644 --- a/server/services/zigbee2mqtt/exposes/enumType.js +++ b/server/services/zigbee2mqtt/exposes/enumType.js @@ -78,6 +78,15 @@ addMapping('action', BUTTON_STATUS.SHORT_RELEASE, 'short_release'); addMapping('action', BUTTON_STATUS.LONG_RELEASE, 'long_release'); addMapping('action', BUTTON_STATUS.DOUBLE_PRESS, 'double_press'); +addMapping('action', BUTTON_STATUS.TOGGLE, 'toggle'); +addMapping('action', BUTTON_STATUS.TOGGLE_HOLD, 'toggle_hold'); +addMapping('action', BUTTON_STATUS.BRIGHTNESS_UP_CLICK, 'brightness_up_click'); +addMapping('action', BUTTON_STATUS.BRIGHTNESS_UP_HOLD, 'brightness_up_hold'); +addMapping('action', BUTTON_STATUS.BRIGHTNESS_UP_RELEASE, 'brightness_up_release'); +addMapping('action', BUTTON_STATUS.BRIGHTNESS_DOWN_CLICK, 'brightness_down_click'); +addMapping('action', BUTTON_STATUS.BRIGHTNESS_DOWN_HOLD, 'brightness_down_hold'); +addMapping('action', BUTTON_STATUS.BRIGHTNESS_DOWN_RELEASE, 'brightness_down_release'); + addMapping('state', COVER_STATE.OPEN, 'OPEN'); addMapping('state', COVER_STATE.CLOSE, 'CLOSE'); addMapping('state', COVER_STATE.STOP, 'STOP'); diff --git a/server/services/zigbee2mqtt/exposes/numericType.js b/server/services/zigbee2mqtt/exposes/numericType.js index aa142a0ad2..d3498fa0cb 100644 --- a/server/services/zigbee2mqtt/exposes/numericType.js +++ b/server/services/zigbee2mqtt/exposes/numericType.js @@ -270,73 +270,73 @@ module.exports = { }, }, // Lixee TIC Device - BASE: { + EAST: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EAST, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - ISOUSC: { + EAIT: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EAIT, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - HCHC: { + EASF01: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF01, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - HCHP: { + EASF02: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF02, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - BBRHCJW: { + EASF03: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF03, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - BBRHPJW: { + EASF04: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF04, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - BBRHCJR: { + EASF05: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF05, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - BBRHPJR: { + EASF06: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF06, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -344,8 +344,8 @@ module.exports = { }, EASF07: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF07, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -353,8 +353,8 @@ module.exports = { }, EASF08: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF08, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -362,8 +362,8 @@ module.exports = { }, EASF09: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF09, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -371,136 +371,142 @@ module.exports = { }, EASF10: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASF10, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - IINST: { + PREF: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.PREF, + unit: DEVICE_FEATURE_UNITS.KILOVOLT_AMPERE, }, }, - IINST2: { + PCOUP: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.PCOUP, + unit: DEVICE_FEATURE_UNITS.KILOVOLT_AMPERE, }, }, - IINST3: { + VTIC: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.VTIC, }, }, - IMAX: { + CCASN: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCASN, + unit: DEVICE_FEATURE_UNITS.WATT, }, }, - IMAX2: { + 'CCASN-1': { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCASN_1, + unit: DEVICE_FEATURE_UNITS.WATT, }, }, - IMAX3: { + UMOY1: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY1, + unit: DEVICE_FEATURE_UNITS.VOLT, }, }, - PMAX: { + UMOY2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.WATT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY2, + unit: DEVICE_FEATURE_UNITS.VOLT, }, }, - SMAXN: { + UMOY3: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.UMOY2, + unit: DEVICE_FEATURE_UNITS.VOLT, }, }, - SMAXN2: { + ERQ1: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ1, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, }, }, - SMAXN3: { + ERQ2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ2, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, }, }, - 'SMAXN-1': { + ERQ3: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ3, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, }, }, - 'SMAXN2-1': { + ERQ4: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ERQ4, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, }, }, - 'SMAXN3-1': { + IRMS1: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS1, + unit: DEVICE_FEATURE_UNITS.AMPERE, }, }, - PAPP: { + IRMS2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS2, + unit: DEVICE_FEATURE_UNITS.AMPERE, }, }, - SINSTS: { + IRMS3: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IRMS3, + unit: DEVICE_FEATURE_UNITS.AMPERE, }, }, - SINSTS2: { + URMS1: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS1, + unit: DEVICE_FEATURE_UNITS.VOLT, }, }, - SINSTS3: { + URMS2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS2, + unit: DEVICE_FEATURE_UNITS.VOLT, + }, + }, + URMS3: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.URMS3, + unit: DEVICE_FEATURE_UNITS.VOLT, }, }, EASD01: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD01, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -508,8 +514,8 @@ module.exports = { }, EASD02: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD02, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -517,8 +523,8 @@ module.exports = { }, EASD03: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD03, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, @@ -526,204 +532,285 @@ module.exports = { }, EASD04: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.EASD04, unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - URMS1: { + NTARF: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.NTARF, }, }, - URMS2: { + CCAIN: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCAIN, + unit: DEVICE_FEATURE_UNITS.WATT, }, }, - URMS3: { + 'CCAIN-1': { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.CCAIN_1, + unit: DEVICE_FEATURE_UNITS.WATT, }, }, SINSTI: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTI, unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, SMAXIN: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXIN, unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, 'SMAXIN-1': { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXIN_1, unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - CCAIN: { + RELAIS: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.WATT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.BINARY, }, }, - 'CCAIN-1': { + SMAXN: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.WATT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - CCASN: { + SMAXN2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.WATT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN2, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - 'CCASN-1': { + SMAXN3: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.WATT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN3, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - UMOY1: { + SINSTS: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - UMOY2: { + SINSTS2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS2, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - UMOY3: { + SINSTS3: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SINSTS3, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - ERQ2: { + 'SMAXN-1': { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN_1, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - ERQ3: { + 'SMAXN2-1': { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN2_1, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - ERQ4: { + 'SMAXN3-1': { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.SMAXN3_1, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, }, }, - ERQ1: { + HHPHC: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, - unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE_REACTIVE, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.HHPHC, }, }, - EAIT: { + IMAX: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, - unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX, + unit: DEVICE_FEATURE_UNITS.AMPERE, }, }, - NTARF: { + IMAX2: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX2, + unit: DEVICE_FEATURE_UNITS.AMPERE, }, }, - IRMS1: { + IMAX3: { feature: { - category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.IMAX3, unit: DEVICE_FEATURE_UNITS.AMPERE, }, }, - PREF: { + ADPS: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ADPS, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + ADIR1: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ADIR1, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + ADIR2: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ADIR2, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + ADIR3: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TELEINFORMATION, + type: DEVICE_FEATURE_TYPES.TELEINFORMATION.ADIR3, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + BASE: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.KILOVOLT_AMPERE, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, + min: 0, + max: 1000000, }, }, - PCOUP: { + ISOUSC: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, - unit: DEVICE_FEATURE_UNITS.KILOVOLT_AMPERE, + unit: DEVICE_FEATURE_UNITS.AMPERE, + min: 0, + max: 1000000, }, }, - RELAIS: { + HCHC: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, - type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.BINARY, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, + min: 0, + max: 1000000, }, }, - EAST: { + HCHP: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - EASF01: { + BBRHCJW: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - EASF02: { + BBRHPJW: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, - EASF03: { + BBRHCJR: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, + min: 0, + max: 1000000, + }, + }, + BBRHPJR: { feature: { category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.INDEX, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, min: 0, max: 1000000, }, }, + IINST: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + IINST2: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + IINST3: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.CURRENT, + unit: DEVICE_FEATURE_UNITS.AMPERE, + }, + }, + PMAX: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, + unit: DEVICE_FEATURE_UNITS.WATT, + }, + }, + PAPP: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.ENERGY_SENSOR, + type: DEVICE_FEATURE_TYPES.ENERGY_SENSOR.POWER, + unit: DEVICE_FEATURE_UNITS.VOLT_AMPERE, + }, + }, // End of Lixee TIC device voc: { feature: { diff --git a/server/services/zigbee2mqtt/lib/index.js b/server/services/zigbee2mqtt/lib/index.js index 058c3d79d3..15f70704d5 100644 --- a/server/services/zigbee2mqtt/lib/index.js +++ b/server/services/zigbee2mqtt/lib/index.js @@ -4,6 +4,7 @@ const { getConfiguration } = require('./getConfiguration'); const { saveConfiguration } = require('./saveConfiguration'); const { disconnect } = require('./disconnect'); const { handleMqttMessage } = require('./handleMqttMessage'); +const { publish } = require('./publish'); const { getDiscoveredDevices } = require('./getDiscoveredDevices'); const { findMatchingExpose } = require('./findMatchingExpose'); const { readValue } = require('./readValue'); @@ -70,6 +71,7 @@ Zigbee2mqttManager.prototype.getConfiguration = getConfiguration; Zigbee2mqttManager.prototype.saveConfiguration = saveConfiguration; Zigbee2mqttManager.prototype.disconnect = disconnect; Zigbee2mqttManager.prototype.handleMqttMessage = handleMqttMessage; +Zigbee2mqttManager.prototype.publish = publish; Zigbee2mqttManager.prototype.getDiscoveredDevices = getDiscoveredDevices; Zigbee2mqttManager.prototype.findMatchingExpose = findMatchingExpose; Zigbee2mqttManager.prototype.readValue = readValue; diff --git a/server/services/zigbee2mqtt/lib/publish.js b/server/services/zigbee2mqtt/lib/publish.js new file mode 100644 index 0000000000..c7a4b58683 --- /dev/null +++ b/server/services/zigbee2mqtt/lib/publish.js @@ -0,0 +1,24 @@ +const logger = require('../../../utils/logger'); +const { ServiceNotConfiguredError } = require('../../../utils/coreErrors'); + +/** + * @description Publish a MQTT message. + * @param {string} topic - MQTT Topic. + * @param {string} message - MQTT message. + * @example zigbee2mqtt.publish('zigbee2mqtt/test', '{}'); + */ +function publish(topic, message) { + if (!this.mqttClient) { + throw new ServiceNotConfiguredError('MQTT is not configured.'); + } + logger.debug(`Publishing MQTT message on topic ${topic}`); + this.mqttClient.publish(topic, message, undefined, (err) => { + if (err) { + logger.warn(`MQTT - Error publishing to ${topic} : ${err}`); + } + }); +} + +module.exports = { + publish, +}; diff --git a/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js b/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js index e6913af973..8eeca35d4e 100644 --- a/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js +++ b/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js @@ -205,6 +205,18 @@ describe('gateway.forwardMessageToOpenAI', () => { intent: 'temperature-sensor.get-in-room', }); }); + it('should do nothing, no-response was sent', async () => { + gateway.gladysGatewayClient.openAIAsk = fake.resolves({ + type: 'NO_RESPONSE', + answer: '', + room: '', + }); + const classification = await gateway.forwardMessageToOpenAI({ message, previousQuestions, context }); + expect(classification).to.deep.equal({ + intent: undefined, + }); + assert.notCalled(messageManager.reply); + }); it('should start scene from OpenAI', async () => { gateway.gladysGatewayClient.openAIAsk = fake.resolves({ type: 'SCENE_START', diff --git a/server/test/lib/scene/actions/scene.action.sendZigbee2MqttMessage.test.js b/server/test/lib/scene/actions/scene.action.sendZigbee2MqttMessage.test.js new file mode 100644 index 0000000000..a9b710cf60 --- /dev/null +++ b/server/test/lib/scene/actions/scene.action.sendZigbee2MqttMessage.test.js @@ -0,0 +1,87 @@ +const { fake, assert } = require('sinon'); +const EventEmitter = require('events'); + +const { ACTIONS } = require('../../../../utils/constants'); +const { executeActions } = require('../../../../lib/scene/scene.executeActions'); + +const StateManager = require('../../../../lib/state'); + +const event = new EventEmitter(); + +describe('scene.send-zigbee2mqtt-message', () => { + it('should send message with value injected from device get-value', async () => { + const stateManager = new StateManager(event); + stateManager.setState('deviceFeature', 'my-device-feature', { + category: 'light', + type: 'binary', + last_value: 15, + }); + const zigbee2MqttService = { + device: { + publish: fake.resolves(null), + }, + }; + const service = { + getService: fake.returns(zigbee2MqttService), + }; + const scope = {}; + await executeActions( + { stateManager, event, service }, + [ + [ + { + type: ACTIONS.DEVICE.GET_VALUE, + device_feature: 'my-device-feature', + }, + ], + [ + { + type: ACTIONS.ZIGBEE2MQTT.SEND, + topic: '/my/mqtt/topic', + message: 'Temperature in the living room is {{0.0.last_value}} °C.', + }, + ], + ], + scope, + ); + assert.calledWith(zigbee2MqttService.device.publish, '/my/mqtt/topic', 'Temperature in the living room is 15 °C.'); + }); + it('should send message with value injected from http-request', async () => { + const stateManager = new StateManager(event); + const http = { + request: fake.resolves({ result: [15], error: null }), + }; + const zigbee2MqttService = { + device: { + publish: fake.resolves(null), + }, + }; + const service = { + getService: fake.returns(zigbee2MqttService), + }; + const scope = {}; + await executeActions( + { stateManager, event, service, http }, + [ + [ + { + type: ACTIONS.HTTP.REQUEST, + method: 'post', + url: 'http://test.test', + body: '{"toto":"toto"}', + headers: [], + }, + ], + [ + { + type: ACTIONS.ZIGBEE2MQTT.SEND, + topic: '/my/mqtt/topic', + message: 'Temperature in the living room is {{0.0.result.[0]}} °C.', + }, + ], + ], + scope, + ); + assert.calledWith(zigbee2MqttService.device.publish, '/my/mqtt/topic', 'Temperature in the living room is 15 °C.'); + }); +}); diff --git a/server/test/lib/scene/scene.checkTrigger.test.js b/server/test/lib/scene/scene.checkTrigger.test.js index 4ab3be0d81..7b43695395 100644 --- a/server/test/lib/scene/scene.checkTrigger.test.js +++ b/server/test/lib/scene/scene.checkTrigger.test.js @@ -57,80 +57,6 @@ describe('scene.checkTrigger', () => { sinon.reset(); }); - it('should execute scene', async () => { - sceneManager.addScene({ - selector: 'my-scene', - active: true, - actions: [ - [ - { - type: ACTIONS.LIGHT.TURN_ON, - devices: ['light-1'], - }, - ], - ], - triggers: [ - { - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - value: 12, - operator: '=', - }, - ], - }); - sceneManager.checkTrigger({ - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - last_value: 12, - }); - return new Promise((resolve, reject) => { - sceneManager.queue.start(() => { - try { - assert.calledOnce(device.setValue); - resolve(); - } catch (e) { - reject(e); - } - }); - }); - }); - it('should not execute scene, scene not active', async () => { - sceneManager.addScene({ - selector: 'my-scene', - active: false, - actions: [ - [ - { - type: ACTIONS.LIGHT.TURN_ON, - devices: ['light-1'], - }, - ], - ], - triggers: [ - { - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - value: 12, - operator: '=', - }, - ], - }); - sceneManager.checkTrigger({ - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - last_value: 12, - }); - return new Promise((resolve, reject) => { - sceneManager.queue.start(() => { - try { - assert.notCalled(device.setValue); - resolve(); - } catch (e) { - reject(e); - } - }); - }); - }); it('should execute scene', async () => { const addedScene = sceneManager.addScene({ selector: 'my-scene', @@ -489,122 +415,6 @@ describe('scene.checkTrigger', () => { }); }); }); - - it('should not execute scene, condition not verified', async () => { - sceneManager.addScene({ - selector: 'my-scene', - active: true, - actions: [ - [ - { - type: ACTIONS.LIGHT.TURN_ON, - devices: ['light-1'], - }, - ], - ], - triggers: [ - { - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - value: 12, - operator: '=', - }, - ], - }); - sceneManager.checkTrigger({ - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - last_value: 14, - }); - return new Promise((resolve, reject) => { - sceneManager.queue.start(() => { - try { - assert.notCalled(device.setValue); - resolve(); - } catch (e) { - reject(e); - } - }); - }); - }); - it('should not execute scene, threshold already passed', async () => { - sceneManager.addScene({ - selector: 'my-scene', - active: true, - actions: [ - [ - { - type: ACTIONS.LIGHT.TURN_ON, - devices: ['light-1'], - }, - ], - ], - triggers: [ - { - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - value: 12, - operator: '>', - threshold_only: true, - }, - ], - }); - sceneManager.checkTrigger({ - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - previous_value: 14, - last_value: 14, - }); - return new Promise((resolve, reject) => { - sceneManager.queue.start(() => { - try { - assert.notCalled(device.setValue); - resolve(); - } catch (e) { - reject(e); - } - }); - }); - }); - it('should execute scene, threshold passed for the first time', async () => { - sceneManager.addScene({ - selector: 'my-scene', - active: true, - actions: [ - [ - { - type: ACTIONS.LIGHT.TURN_ON, - devices: ['light-1'], - }, - ], - ], - triggers: [ - { - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - value: 12, - operator: '>', - threshold_only: true, - }, - ], - }); - sceneManager.checkTrigger({ - type: EVENTS.DEVICE.NEW_STATE, - device_feature: 'light-1', - previous_value: 11, - last_value: 14, - }); - return new Promise((resolve, reject) => { - sceneManager.queue.start(() => { - try { - assert.calledOnce(device.setValue); - resolve(); - } catch (e) { - reject(e); - } - }); - }); - }); it('should not execute scene, event not matching', async () => { sceneManager.addScene({ selector: 'my-scene', diff --git a/server/test/lib/scene/triggers/scene.trigger.alarmMode.test.js b/server/test/lib/scene/triggers/scene.trigger.alarmMode.test.js index 283bdf61f7..e8db1371e4 100644 --- a/server/test/lib/scene/triggers/scene.trigger.alarmMode.test.js +++ b/server/test/lib/scene/triggers/scene.trigger.alarmMode.test.js @@ -215,6 +215,40 @@ describe('Scene.triggers.alarmMode', () => { }); }); }); + it('should execute scene with alarm.too-many-codes-tests trigger', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_OFF, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.ALARM.TOO_MANY_CODES_TESTS, + house: 'house-1', + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.ALARM.TOO_MANY_CODES_TESTS, + house: 'house-1', + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.calledOnce(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); it('should not execute scene (house not matching)', async () => { sceneManager.addScene({ selector: 'my-scene', diff --git a/server/test/lib/scene/triggers/scene.trigger.deviceNewState.test.js b/server/test/lib/scene/triggers/scene.trigger.deviceNewState.test.js new file mode 100644 index 0000000000..374661573f --- /dev/null +++ b/server/test/lib/scene/triggers/scene.trigger.deviceNewState.test.js @@ -0,0 +1,470 @@ +const sinon = require('sinon'); +const { expect } = require('chai'); +const EventEmitter = require('events'); +const Promise = require('bluebird'); + +const { assert, fake } = sinon; + +const { EVENTS, ACTIONS } = require('../../../../utils/constants'); +const SceneManager = require('../../../../lib/scene'); +const StateManager = require('../../../../lib/state'); + +const event = new EventEmitter(); + +describe('scene.triggers.deviceNewState', () => { + let sceneManager; + let device; + + const brain = {}; + + const service = { + getService: fake.returns({ + device: { + subscribe: fake.returns(null), + }, + }), + }; + + beforeEach(() => { + const house = { + get: fake.resolves([]), + }; + + device = { + setValue: fake.resolves(null), + }; + + const scheduler = { + scheduleJob: (date, callback) => { + return { + callback, + date, + cancel: () => {}, + }; + }, + }; + + brain.addNamedEntity = fake.returns(null); + brain.removeNamedEntity = fake.returns(null); + + const stateManager = new StateManager(); + stateManager.setState('deviceFeature', 'light-1', { + last_value: 14, + }); + sceneManager = new SceneManager(stateManager, event, device, {}, {}, house, {}, {}, {}, scheduler, brain, service); + }); + + afterEach(() => { + sinon.reset(); + }); + + it('should execute scene', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '=', + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + last_value: 12, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.calledOnce(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should not execute scene, scene not active', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: false, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '=', + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + last_value: 12, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.notCalled(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should not execute scene, condition not verified', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '=', + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.notCalled(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should not execute scene, device feature is not the same', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '=', + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-2', + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.notCalled(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should not execute scene, threshold already passed', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '>', + threshold_only: true, + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 14, + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.notCalled(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should execute scene, threshold passed for the first time', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '>', + threshold_only: true, + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 11, + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.calledOnce(device.setValue); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should start timer to check later for state and not follow current scene', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '>', + threshold_only: true, + for_duration: 10 * 60 * 1000, + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 11, + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(() => { + try { + assert.notCalled(device.setValue); + expect(sceneManager.checkTriggersDurationTimer.size).to.equal(1); + sceneManager.checkTriggersDurationTimer.forEach((value, timeoutKey) => { + expect(timeoutKey).to.equal('device.new-state.my-scene.light-1:>:12'); + clearTimeout(value); + }); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should start timer to check now and condition should still be valid on second call', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '>', + threshold_only: true, + for_duration: 0, // now + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 11, + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(async () => { + try { + await Promise.delay(5); + assert.calledOnce(device.setValue); + expect(sceneManager.checkTriggersDurationTimer.size).to.equal(0); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should start timer to check now and re-send new value still validating the condition', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '>', + threshold_only: false, + for_duration: 5, + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 11, + last_value: 14, + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 14, + last_value: 14, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(async () => { + try { + await Promise.delay(10); + assert.calledOnce(device.setValue); + expect(sceneManager.checkTriggersDurationTimer.size).to.equal(0); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); + it('should start timer to check now and condition should not be valid on second call', async () => { + sceneManager.addScene({ + selector: 'my-scene', + active: true, + actions: [ + [ + { + type: ACTIONS.LIGHT.TURN_ON, + devices: ['light-1'], + }, + ], + ], + triggers: [ + { + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + value: 12, + operator: '>', + threshold_only: true, + for_duration: 10, // In 10ms + }, + ], + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 11, + last_value: 14, + }); + sceneManager.checkTrigger({ + type: EVENTS.DEVICE.NEW_STATE, + device_feature: 'light-1', + previous_value: 14, + last_value: 5, + }); + return new Promise((resolve, reject) => { + sceneManager.queue.start(async () => { + try { + await Promise.delay(5); + assert.notCalled(device.setValue); + expect(sceneManager.checkTriggersDurationTimer.size).to.equal(0); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + }); +}); diff --git a/server/test/security/user.test.js b/server/test/security/user.test.js new file mode 100644 index 0000000000..3bb9aaa16b --- /dev/null +++ b/server/test/security/user.test.js @@ -0,0 +1,16 @@ +const { expect } = require('chai'); +const { authenticatedRequest } = require('../controllers/request.test'); + +describe('/api/v1/user/', () => { + it('should return all users without password', async () => { + await authenticatedRequest + .get('/api/v1/user?fields=password') + .expect('Content-Type', /json/) + .expect(200) + .then((res) => { + res.body.forEach((user) => { + expect(user).to.not.have.property('password'); + }); + }); + }); +}); diff --git a/server/test/services/zigbee2mqtt/lib/publish.test.js b/server/test/services/zigbee2mqtt/lib/publish.test.js new file mode 100644 index 0000000000..b658bcfe56 --- /dev/null +++ b/server/test/services/zigbee2mqtt/lib/publish.test.js @@ -0,0 +1,76 @@ +const sinon = require('sinon'); +const { expect } = require('chai'); + +const { assert, fake } = sinon; + +const Zigbee2mqttManager = require('../../../../services/zigbee2mqtt/lib'); +const { ServiceNotConfiguredError } = require('../../../../utils/coreErrors'); + +const serviceId = 'f87b7af2-ca8e-44fc-b754-444354b42fee'; + +const gladys = { + job: { + wrapper: (type, func) => { + return async () => { + return func(); + }; + }, + }, + variable: { + getValue: fake.resolves('toto'), + }, + event: { + emit: fake.returns(null), + }, +}; + +describe('zigbee2mqttManager.publish', () => { + beforeEach(() => { + sinon.reset(); + }); + + afterEach(() => { + sinon.reset(); + }); + + it('should publish MQTT message', () => { + const mqttClient = { + publish: fake.returns(null), + }; + const mqttLibrary = { + connect: fake.returns(mqttClient), + }; + const zigbee2mqttManager = new Zigbee2mqttManager(gladys, mqttLibrary, serviceId); + zigbee2mqttManager.mqttClient = mqttClient; + zigbee2mqttManager.publish('toto', 'message'); + assert.calledWith(mqttClient.publish, 'toto', 'message'); + }); + it('should publish MQTT message with error', () => { + const mqttClient = { + publish: (topic, message, random, cb) => { + cb('toto'); + }, + }; + const mqttLibrary = { + connect: fake.returns(mqttClient), + }; + const zigbee2mqttManager = new Zigbee2mqttManager(gladys, mqttLibrary, serviceId); + zigbee2mqttManager.mqttClient = mqttClient; + zigbee2mqttManager.publish('toto', 'mesage'); + }); + it('should not publish MQTT message', async () => { + const mqttLibrary = { + connect: fake.returns(null), + }; + const zigbee2mqttManager = new Zigbee2mqttManager(gladys, mqttLibrary, serviceId); + try { + zigbee2mqttManager.publish('toto', 'mesage'); + } catch (e) { + expect(e).instanceOf(ServiceNotConfiguredError); + + return; + } + + assert.fail(); + }); +}); diff --git a/server/utils/constants.js b/server/utils/constants.js index 891e1fbd94..bf5315ef89 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -55,6 +55,14 @@ const BUTTON_STATUS = { SHORT_RELEASE: 49, LONG_RELEASE: 50, DOUBLE_PRESS: 51, + TOGGLE: 52, + TOGGLE_HOLD: 53, + BRIGHTNESS_UP_CLICK: 54, + BRIGHTNESS_UP_HOLD: 55, + BRIGHTNESS_UP_RELEASE: 56, + BRIGHTNESS_DOWN_CLICK: 57, + BRIGHTNESS_DOWN_HOLD: 58, + BRIGHTNESS_DOWN_RELEASE: 59, }; const COVER_STATE = { @@ -404,6 +412,9 @@ const ACTIONS = { MQTT: { SEND: 'mqtt.send', }, + ZIGBEE2MQTT: { + SEND: 'zigbee2mqtt.send', + }, MUSIC: { PLAY_NOTIFICATION: 'music.play-notification', }, @@ -480,6 +491,7 @@ const DEVICE_FEATURE_CATEGORIES = { SWITCH: 'switch', SPEED_SENSOR: 'speed-sensor', TAMPER: 'tamper', + TELEINFORMATION: 'teleinformation', TELEVISION: 'television', TEMPERATURE_SENSOR: 'temperature-sensor', THERMOSTAT: 'thermostat', @@ -619,6 +631,7 @@ const DEVICE_FEATURE_TYPES = { }, BUTTON: { CLICK: 'click', + PUSH: 'push', }, SIGNAL: { QUALITY: 'integer', @@ -686,6 +699,66 @@ const DEVICE_FEATURE_TYPES = { INDEX: 'index', DAILY_CONSUMPTION: 'daily-consumption', }, + TELEINFORMATION: { + BINARY: 'binary', + EAST: 'east', + EAIT: 'eait', + EASF01: 'easf01', + EASF02: 'easf02', + EASF03: 'easf03', + EASF04: 'easf04', + EASF05: 'easf05', + EASF06: 'easf06', + EASF07: 'easf07', + EASF08: 'easf08', + EASF09: 'easf09', + EASF10: 'easf10', + PREF: 'pref', + PCOUP: 'pcoup', + VTIC: 'vtic', + CCASN: 'ccasn', + CCASN_1: 'ccasn_1', + UMOY1: 'umoy1', + UMOY2: 'umoy2', + UMOY3: 'umoy3', + ERQ1: 'erq1', + ERQ2: 'erq2', + ERQ3: 'erq3', + ERQ4: 'erq4', + IRMS1: 'irms1', + IRMS2: 'irms2', + IRMS3: 'irms3', + URMS1: 'urms1', + URMS2: 'urms2', + URMS3: 'urms3', + EASD01: 'easd01', + EASD02: 'easd02', + EASD03: 'easd03', + EASD04: 'easd04', + NTARF: 'ntarf', + CCAIN: 'ccain', + CCAIN_1: 'ccain_1', + SINSTI: 'sinsti', + SMAXIN: 'smaxin', + SMAXIN_1: 'smaxin_1', + SMAXN: 'smaxn', + SMAXN2: 'smaxn2', + SMAXN3: 'smaxn3', + SINSTS: 'sinsts', + SINSTS2: 'sinsts2', + SINSTS3: 'sinsts3', + SMAXN_1: 'smaxn_1', + SMAXN2_1: 'smaxn2_1', + SMAXN3_1: 'smaxn3_1', + HHPHC: 'hhphc', + IMAX: 'imax', + ADPS: 'adps', + IMAX2: 'imax2', + IMAX3: 'imax3', + ADIR1: 'adir1', + ADIR2: 'adir2', + ADIR3: 'adir3', + }, SPEED_SENSOR: { DECIMAL: 'decimal', INTEGER: 'integer',