From 97100b0fd81fb9c6edfbf6a1bacf99158952c44c Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Sun, 19 Sep 2021 08:35:22 +0200 Subject: [PATCH 1/9] Zigbee2mqtt remove device list --- server/services/zigbee2mqtt/model/AXIS.js | 15 - server/services/zigbee2mqtt/model/Adeo.js | 17 -- .../services/zigbee2mqtt/model/AduroSmart.js | 16 -- server/services/zigbee2mqtt/model/Airam.js | 16 -- server/services/zigbee2mqtt/model/Anchor.js | 15 - server/services/zigbee2mqtt/model/Belkin.js | 15 - server/services/zigbee2mqtt/model/Bitron.js | 18 -- .../services/zigbee2mqtt/model/Blaupunkt.js | 15 - server/services/zigbee2mqtt/model/Bosch.js | 16 -- server/services/zigbee2mqtt/model/Calex.js | 16 -- .../services/zigbee2mqtt/model/Centralite.js | 15 - server/services/zigbee2mqtt/model/Climax.js | 17 -- .../zigbee2mqtt/model/CommercialElectric.js | 15 - server/services/zigbee2mqtt/model/Danalock.js | 15 - .../zigbee2mqtt/model/DresdenElektronik.js | 16 -- server/services/zigbee2mqtt/model/EDP.js | 16 -- server/services/zigbee2mqtt/model/ELKO.js | 15 - server/services/zigbee2mqtt/model/EcoSmart.js | 18 -- .../services/zigbee2mqtt/model/Eurotronic.js | 15 - server/services/zigbee2mqtt/model/GE.js | 19 -- .../zigbee2mqtt/model/GMYSmartBulb.js | 15 - server/services/zigbee2mqtt/model/Gira.js | 15 - server/services/zigbee2mqtt/model/Gledopto.js | 26 -- server/services/zigbee2mqtt/model/HEIMAN.js | 27 -- .../services/zigbee2mqtt/model/HamptonBay.js | 15 - server/services/zigbee2mqtt/model/Hive.js | 19 -- server/services/zigbee2mqtt/model/Honyar.js | 15 - server/services/zigbee2mqtt/model/IKEA.js | 43 --- .../services/zigbee2mqtt/model/Iluminize.js | 15 - server/services/zigbee2mqtt/model/Immax.js | 15 - server/services/zigbee2mqtt/model/Innr.js | 42 --- server/services/zigbee2mqtt/model/Iris.js | 17 -- server/services/zigbee2mqtt/model/JIAWEN.js | 15 - server/services/zigbee2mqtt/model/KeenHome.js | 16 -- .../zigbee2mqtt/model/KsentryElectronics.js | 15 - .../services/zigbee2mqtt/model/Leedarson.js | 17 -- server/services/zigbee2mqtt/model/Lidl.js | 25 -- .../services/zigbee2mqtt/model/LivingWise.js | 16 -- server/services/zigbee2mqtt/model/Livolo.js | 15 - server/services/zigbee2mqtt/model/Lonsonho.js | 16 -- server/services/zigbee2mqtt/model/Lupus.js | 16 -- server/services/zigbee2mqtt/model/Meazon.js | 16 -- .../services/zigbee2mqtt/model/MullerLicht.js | 17 -- server/services/zigbee2mqtt/model/NET2GRID.js | 15 - server/services/zigbee2mqtt/model/Nanoleaf.js | 15 - server/services/zigbee2mqtt/model/Netvox.js | 15 - .../services/zigbee2mqtt/model/NinjaBlocks.js | 15 - server/services/zigbee2mqtt/model/Nue_3A.js | 31 -- server/services/zigbee2mqtt/model/Nyce.js | 18 -- server/services/zigbee2mqtt/model/ORVIBO.js | 15 - server/services/zigbee2mqtt/model/OSRAM.js | 37 --- server/services/zigbee2mqtt/model/Oujiabao.js | 15 - .../services/zigbee2mqtt/model/PaulNeuhaus.js | 17 -- server/services/zigbee2mqtt/model/Paulmann.js | 18 -- server/services/zigbee2mqtt/model/Philips.js | 51 ---- server/services/zigbee2mqtt/model/RGBGenie.js | 15 - server/services/zigbee2mqtt/model/ROBB.js | 15 - server/services/zigbee2mqtt/model/SONOFF.js | 18 -- server/services/zigbee2mqtt/model/Salus.js | 15 - server/services/zigbee2mqtt/model/Securifi.js | 15 - server/services/zigbee2mqtt/model/Sengled.js | 21 -- server/services/zigbee2mqtt/model/Sercomm.js | 15 - .../zigbee2mqtt/model/ShenzhenHoma.js | 17 -- .../zigbee2mqtt/model/SmartHomePty.js | 16 -- .../services/zigbee2mqtt/model/SmartThings.js | 29 -- server/services/zigbee2mqtt/model/Stelpro.js | 15 - .../services/zigbee2mqtt/model/Sunricher.js | 15 - server/services/zigbee2mqtt/model/Swann.js | 16 -- server/services/zigbee2mqtt/model/Sylvania.js | 25 -- server/services/zigbee2mqtt/model/TUYATEC.js | 15 - .../zigbee2mqtt/model/ThirdReality.js | 15 - server/services/zigbee2mqtt/model/Trust.js | 18 -- server/services/zigbee2mqtt/model/TuYa.js | 21 -- server/services/zigbee2mqtt/model/Visonic.js | 16 -- server/services/zigbee2mqtt/model/Xiaomi.js | 45 --- server/services/zigbee2mqtt/model/Yale.js | 18 -- server/services/zigbee2mqtt/model/eCosy.js | 15 - server/services/zigbee2mqtt/model/iCasa.js | 15 - server/services/zigbee2mqtt/model/ilux.js | 15 - server/services/zigbee2mqtt/model/index.js | 102 ------- server/services/zigbee2mqtt/utils/features.js | 272 ------------------ .../zigbee2mqtt/utils/loadFeatures.js | 33 --- .../zigbee2mqtt/utils/loadFeatures.test.js | 59 ---- 83 files changed, 1936 deletions(-) delete mode 100644 server/services/zigbee2mqtt/model/AXIS.js delete mode 100644 server/services/zigbee2mqtt/model/Adeo.js delete mode 100644 server/services/zigbee2mqtt/model/AduroSmart.js delete mode 100644 server/services/zigbee2mqtt/model/Airam.js delete mode 100644 server/services/zigbee2mqtt/model/Anchor.js delete mode 100644 server/services/zigbee2mqtt/model/Belkin.js delete mode 100644 server/services/zigbee2mqtt/model/Bitron.js delete mode 100644 server/services/zigbee2mqtt/model/Blaupunkt.js delete mode 100644 server/services/zigbee2mqtt/model/Bosch.js delete mode 100644 server/services/zigbee2mqtt/model/Calex.js delete mode 100644 server/services/zigbee2mqtt/model/Centralite.js delete mode 100644 server/services/zigbee2mqtt/model/Climax.js delete mode 100644 server/services/zigbee2mqtt/model/CommercialElectric.js delete mode 100644 server/services/zigbee2mqtt/model/Danalock.js delete mode 100644 server/services/zigbee2mqtt/model/DresdenElektronik.js delete mode 100644 server/services/zigbee2mqtt/model/EDP.js delete mode 100644 server/services/zigbee2mqtt/model/ELKO.js delete mode 100644 server/services/zigbee2mqtt/model/EcoSmart.js delete mode 100644 server/services/zigbee2mqtt/model/Eurotronic.js delete mode 100644 server/services/zigbee2mqtt/model/GE.js delete mode 100644 server/services/zigbee2mqtt/model/GMYSmartBulb.js delete mode 100644 server/services/zigbee2mqtt/model/Gira.js delete mode 100644 server/services/zigbee2mqtt/model/Gledopto.js delete mode 100644 server/services/zigbee2mqtt/model/HEIMAN.js delete mode 100644 server/services/zigbee2mqtt/model/HamptonBay.js delete mode 100644 server/services/zigbee2mqtt/model/Hive.js delete mode 100644 server/services/zigbee2mqtt/model/Honyar.js delete mode 100644 server/services/zigbee2mqtt/model/IKEA.js delete mode 100644 server/services/zigbee2mqtt/model/Iluminize.js delete mode 100644 server/services/zigbee2mqtt/model/Immax.js delete mode 100644 server/services/zigbee2mqtt/model/Innr.js delete mode 100644 server/services/zigbee2mqtt/model/Iris.js delete mode 100644 server/services/zigbee2mqtt/model/JIAWEN.js delete mode 100644 server/services/zigbee2mqtt/model/KeenHome.js delete mode 100644 server/services/zigbee2mqtt/model/KsentryElectronics.js delete mode 100644 server/services/zigbee2mqtt/model/Leedarson.js delete mode 100644 server/services/zigbee2mqtt/model/Lidl.js delete mode 100644 server/services/zigbee2mqtt/model/LivingWise.js delete mode 100644 server/services/zigbee2mqtt/model/Livolo.js delete mode 100644 server/services/zigbee2mqtt/model/Lonsonho.js delete mode 100644 server/services/zigbee2mqtt/model/Lupus.js delete mode 100644 server/services/zigbee2mqtt/model/Meazon.js delete mode 100644 server/services/zigbee2mqtt/model/MullerLicht.js delete mode 100644 server/services/zigbee2mqtt/model/NET2GRID.js delete mode 100644 server/services/zigbee2mqtt/model/Nanoleaf.js delete mode 100644 server/services/zigbee2mqtt/model/Netvox.js delete mode 100644 server/services/zigbee2mqtt/model/NinjaBlocks.js delete mode 100644 server/services/zigbee2mqtt/model/Nue_3A.js delete mode 100644 server/services/zigbee2mqtt/model/Nyce.js delete mode 100644 server/services/zigbee2mqtt/model/ORVIBO.js delete mode 100644 server/services/zigbee2mqtt/model/OSRAM.js delete mode 100644 server/services/zigbee2mqtt/model/Oujiabao.js delete mode 100644 server/services/zigbee2mqtt/model/PaulNeuhaus.js delete mode 100644 server/services/zigbee2mqtt/model/Paulmann.js delete mode 100644 server/services/zigbee2mqtt/model/Philips.js delete mode 100644 server/services/zigbee2mqtt/model/RGBGenie.js delete mode 100644 server/services/zigbee2mqtt/model/ROBB.js delete mode 100644 server/services/zigbee2mqtt/model/SONOFF.js delete mode 100644 server/services/zigbee2mqtt/model/Salus.js delete mode 100644 server/services/zigbee2mqtt/model/Securifi.js delete mode 100644 server/services/zigbee2mqtt/model/Sengled.js delete mode 100644 server/services/zigbee2mqtt/model/Sercomm.js delete mode 100644 server/services/zigbee2mqtt/model/ShenzhenHoma.js delete mode 100644 server/services/zigbee2mqtt/model/SmartHomePty.js delete mode 100644 server/services/zigbee2mqtt/model/SmartThings.js delete mode 100644 server/services/zigbee2mqtt/model/Stelpro.js delete mode 100644 server/services/zigbee2mqtt/model/Sunricher.js delete mode 100644 server/services/zigbee2mqtt/model/Swann.js delete mode 100644 server/services/zigbee2mqtt/model/Sylvania.js delete mode 100644 server/services/zigbee2mqtt/model/TUYATEC.js delete mode 100644 server/services/zigbee2mqtt/model/ThirdReality.js delete mode 100644 server/services/zigbee2mqtt/model/Trust.js delete mode 100644 server/services/zigbee2mqtt/model/TuYa.js delete mode 100644 server/services/zigbee2mqtt/model/Visonic.js delete mode 100644 server/services/zigbee2mqtt/model/Xiaomi.js delete mode 100644 server/services/zigbee2mqtt/model/Yale.js delete mode 100644 server/services/zigbee2mqtt/model/eCosy.js delete mode 100644 server/services/zigbee2mqtt/model/iCasa.js delete mode 100644 server/services/zigbee2mqtt/model/ilux.js delete mode 100644 server/services/zigbee2mqtt/model/index.js delete mode 100644 server/services/zigbee2mqtt/utils/features.js delete mode 100644 server/services/zigbee2mqtt/utils/loadFeatures.js delete mode 100644 server/test/services/zigbee2mqtt/utils/loadFeatures.test.js diff --git a/server/services/zigbee2mqtt/model/AXIS.js b/server/services/zigbee2mqtt/model/AXIS.js deleted file mode 100644 index ad300030f1..0000000000 --- a/server/services/zigbee2mqtt/model/AXIS.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * AXIS managed models. - */ -const AXIS = { - brand: 'AXIS', - models: { - 'GR-ZB01-W': [features.door], // curtain / shuttle - }, -}; - -module.exports = { - AXIS, -}; diff --git a/server/services/zigbee2mqtt/model/Adeo.js b/server/services/zigbee2mqtt/model/Adeo.js deleted file mode 100644 index df787d860a..0000000000 --- a/server/services/zigbee2mqtt/model/Adeo.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Adeo managed models. - */ -const Adeo = { - brand: 'Adeo', - models: { - '9CZA-A806ST-Q1A': [features.light, features.brightness, features.color_temperature, features.color], - '9CZA-M350ST-Q1A': [features.light, features.brightness, features.color_temperature, features.color], - '9CZA-G1521-Q1A': [features.light, features.brightness, features.color_temperature, features.color], - }, -}; - -module.exports = { - Adeo, -}; diff --git a/server/services/zigbee2mqtt/model/AduroSmart.js b/server/services/zigbee2mqtt/model/AduroSmart.js deleted file mode 100644 index 062761fc3a..0000000000 --- a/server/services/zigbee2mqtt/model/AduroSmart.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * AduroSmart managed models. - */ -const AduroSmart = { - brand: 'AduroSmart', - models: { - '81809': [features.light, features.brightness, features.color_temperature, features.color], - '81825': [features.switch_sensor], - }, -}; - -module.exports = { - AduroSmart, -}; diff --git a/server/services/zigbee2mqtt/model/Airam.js b/server/services/zigbee2mqtt/model/Airam.js deleted file mode 100644 index 319ac33644..0000000000 --- a/server/services/zigbee2mqtt/model/Airam.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Airam managed models. - */ -const Airam = { - brand: 'Airam', - models: { - '4713407': [features.light, features.brightness], - 'AIRAM-CTR.U': [features.switch_sensor], - }, -}; - -module.exports = { - Airam, -}; diff --git a/server/services/zigbee2mqtt/model/Anchor.js b/server/services/zigbee2mqtt/model/Anchor.js deleted file mode 100644 index dbd130523e..0000000000 --- a/server/services/zigbee2mqtt/model/Anchor.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Anchor managed models. - */ -const Anchor = { - brand: 'Anchor', - models: { - '67200BL': [features.switch], - }, -}; - -module.exports = { - Anchor, -}; diff --git a/server/services/zigbee2mqtt/model/Belkin.js b/server/services/zigbee2mqtt/model/Belkin.js deleted file mode 100644 index 2b253fce2c..0000000000 --- a/server/services/zigbee2mqtt/model/Belkin.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Belkin managed models. - */ -const Belkin = { - brand: 'Belkin', - models: { - F7C033: [features.light, features.brightness], - }, -}; - -module.exports = { - Belkin, -}; diff --git a/server/services/zigbee2mqtt/model/Bitron.js b/server/services/zigbee2mqtt/model/Bitron.js deleted file mode 100644 index c11f906f00..0000000000 --- a/server/services/zigbee2mqtt/model/Bitron.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Bitron managed models. - */ -const Bitron = { - brand: 'Bitron', - models: { - 'AV2010/34': [features.switch_sensor], - 'AV2010/22': [features.motion], - 'AV2010/25': [features.switch, features.power], - 'AV2010/32': [features.switch_sensor], // features.heating], - }, -}; - -module.exports = { - Bitron, -}; diff --git a/server/services/zigbee2mqtt/model/Blaupunkt.js b/server/services/zigbee2mqtt/model/Blaupunkt.js deleted file mode 100644 index c007b762fa..0000000000 --- a/server/services/zigbee2mqtt/model/Blaupunkt.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Blaupunkt managed models. - */ -const Blaupunkt = { - brand: 'Blaupunkt', - models: { - 'SCM-S1': [features.door], // curtain / shutter - }, -}; - -module.exports = { - Blaupunkt, -}; diff --git a/server/services/zigbee2mqtt/model/Bosch.js b/server/services/zigbee2mqtt/model/Bosch.js deleted file mode 100644 index c9139ad90e..0000000000 --- a/server/services/zigbee2mqtt/model/Bosch.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Bosch managed models. - */ -const Bosch = { - brand: 'Bosch', - models: { - 'RADON TriTech ZB': [features.temperature, features.motion], - 'ISW-ZPR1-WP13': [features.temperature, features.motion], - }, -}; - -module.exports = { - Bosch, -}; diff --git a/server/services/zigbee2mqtt/model/Calex.js b/server/services/zigbee2mqtt/model/Calex.js deleted file mode 100644 index 5a1c2d1f1f..0000000000 --- a/server/services/zigbee2mqtt/model/Calex.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Calex managed models. - */ -const Calex = { - brand: 'Calex', - models: { - '421786': [features.light, features.brightness], - '421792': [features.light, features.brightness, features.color_temperature, features.color], - }, -}; - -module.exports = { - Calex, -}; diff --git a/server/services/zigbee2mqtt/model/Centralite.js b/server/services/zigbee2mqtt/model/Centralite.js deleted file mode 100644 index 12d03fcfd9..0000000000 --- a/server/services/zigbee2mqtt/model/Centralite.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Centralite managed models. - */ -const Centralite = { - brand: 'Centralite', - models: { - '4256251-RZHAC': [features.switch, features.power], - }, -}; - -module.exports = { - Centralite, -}; diff --git a/server/services/zigbee2mqtt/model/Climax.js b/server/services/zigbee2mqtt/model/Climax.js deleted file mode 100644 index 72fb32f4c6..0000000000 --- a/server/services/zigbee2mqtt/model/Climax.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Climax managed models. - */ -const Climax = { - brand: 'Climax', - models: { - 'PSS-23ZBS': [features.switch], - 'SCM-5ZBS': [features.door], // curtain / shutter - 'PSM-29ZBSR': [features.switch], - }, -}; - -module.exports = { - Climax, -}; diff --git a/server/services/zigbee2mqtt/model/CommercialElectric.js b/server/services/zigbee2mqtt/model/CommercialElectric.js deleted file mode 100644 index 2ee9878d72..0000000000 --- a/server/services/zigbee2mqtt/model/CommercialElectric.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Commercial Electric managed models. - */ -const CommercialElectric = { - brand: 'Commercial Electric', - models: { - '53170161': [features.light, features.brightness, features.color_temperature], - }, -}; - -module.exports = { - CommercialElectric, -}; diff --git a/server/services/zigbee2mqtt/model/Danalock.js b/server/services/zigbee2mqtt/model/Danalock.js deleted file mode 100644 index b7a820b9c7..0000000000 --- a/server/services/zigbee2mqtt/model/Danalock.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Danalock managed models. - */ -const Danalock = { - brand: 'Danalock', - models: { - 'V3-BTZB': [features.door], - }, -}; - -module.exports = { - Danalock, -}; diff --git a/server/services/zigbee2mqtt/model/DresdenElektronik.js b/server/services/zigbee2mqtt/model/DresdenElektronik.js deleted file mode 100644 index 6db98273b4..0000000000 --- a/server/services/zigbee2mqtt/model/DresdenElektronik.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Dresden Elektronik managed models. - */ -const DresdenElektronik = { - brand: 'Dresden Elektronik', - models: { - Mega23M12: [features.light, features.brightness, features.color_temperature, features.color], - 'XVV-Mega23M12': [features.light, features.brightness, features.color_temperature], - }, -}; - -module.exports = { - DresdenElektronik, -}; diff --git a/server/services/zigbee2mqtt/model/EDP.js b/server/services/zigbee2mqtt/model/EDP.js deleted file mode 100644 index 09c52c2026..0000000000 --- a/server/services/zigbee2mqtt/model/EDP.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * EDP managed models. - */ -const EDP = { - brand: 'EDP', - models: { - 'PLUG EDP RE:DY': [features.switch, features.power], - 'SWITCH EDP RE:DY': [features.switch], - }, -}; - -module.exports = { - EDP, -}; diff --git a/server/services/zigbee2mqtt/model/ELKO.js b/server/services/zigbee2mqtt/model/ELKO.js deleted file mode 100644 index 1253354f19..0000000000 --- a/server/services/zigbee2mqtt/model/ELKO.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * ELKO managed models. - */ -const ELKO = { - brand: 'ELKO', - models: { - '316GLEDRF': [features.light, features.brightness], - }, -}; - -module.exports = { - ELKO, -}; diff --git a/server/services/zigbee2mqtt/model/EcoSmart.js b/server/services/zigbee2mqtt/model/EcoSmart.js deleted file mode 100644 index 8a46c86477..0000000000 --- a/server/services/zigbee2mqtt/model/EcoSmart.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * EcoSmart managed models. - */ -const EcoSmart = { - brand: 'EcoSmart', - models: { - D1821: [features.light, features.brightness, features.color_temperature, features.color], - D1531: [features.light, features.brightness], - D1532: [features.light, features.brightness], - D1542: [features.light, features.brightness, features.color_temperature], - }, -}; - -module.exports = { - EcoSmart, -}; diff --git a/server/services/zigbee2mqtt/model/Eurotronic.js b/server/services/zigbee2mqtt/model/Eurotronic.js deleted file mode 100644 index 176c5c4802..0000000000 --- a/server/services/zigbee2mqtt/model/Eurotronic.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Eurotronic managed models. - */ -const Eurotronic = { - brand: 'Eurotronic', - models: { - SPZB0001: [features.temperature], // features.heating], - }, -}; - -module.exports = { - Eurotronic, -}; diff --git a/server/services/zigbee2mqtt/model/GE.js b/server/services/zigbee2mqtt/model/GE.js deleted file mode 100644 index 9a70d6fae0..0000000000 --- a/server/services/zigbee2mqtt/model/GE.js +++ /dev/null @@ -1,19 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * GE managed models. - */ -const GE = { - brand: 'GE', - models: { - '22670': [features.light, features.brightness], - '45852GE': [features.light, features.brightness], - '45853GE': [features.switch, features.power], - '45856GE': [features.switch], - '45857GE': [features.light, features.brightness], - }, -}; - -module.exports = { - GE, -}; diff --git a/server/services/zigbee2mqtt/model/GMYSmartBulb.js b/server/services/zigbee2mqtt/model/GMYSmartBulb.js deleted file mode 100644 index b03b809c12..0000000000 --- a/server/services/zigbee2mqtt/model/GMYSmartBulb.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * GMY Smart Bulb managed models. - */ -const GMYSmartBulb = { - brand: 'GMY Smart Bulb', - models: { - B07KG5KF5R: [features.light, features.brightness, features.color_temperature], - }, -}; - -module.exports = { - GMYSmartBulb, -}; diff --git a/server/services/zigbee2mqtt/model/Gira.js b/server/services/zigbee2mqtt/model/Gira.js deleted file mode 100644 index c67ae10670..0000000000 --- a/server/services/zigbee2mqtt/model/Gira.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Gira managed models. - */ -const Gira = { - brand: 'Gira', - models: { - '2430-100': [features.switch_sensor], - }, -}; - -module.exports = { - Gira, -}; diff --git a/server/services/zigbee2mqtt/model/Gledopto.js b/server/services/zigbee2mqtt/model/Gledopto.js deleted file mode 100644 index a2b46a7748..0000000000 --- a/server/services/zigbee2mqtt/model/Gledopto.js +++ /dev/null @@ -1,26 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Gledopto managed models. - */ -const Gledopto = { - brand: 'Gledopto', - models: { - 'GL-C-008': [features.light, features.brightness, features.color_temperature, features.color], - 'GL-S-004Z': [features.light, features.brightness, features.color_temperature], - 'GL-C-006/GL-C-009': [features.light, features.brightness, features.color_temperature], - 'GL-S-007Z': [features.light, features.brightness, features.color_temperature], - 'GL-B-001Z': [features.light, features.brightness, features.color_temperature], - 'GL-G-001Z': [features.light, features.brightness, features.color_temperature, features.color], - 'GL-B-007Z': [features.light, features.brightness, features.color_temperature, features.color], - 'GL-B-008Z': [features.light, features.brightness, features.color_temperature, features.color], - 'GL-D-003Z': [features.light, features.brightness, features.color_temperature, features.color], - 'GL-S-003Z': [features.light, features.brightness, features.color_temperature, features.color], - 'GD-CZ-006': [features.light, features.brightness], - 'GL-FL-004TZ': [features.light, features.brightness, features.color_temperature, features.color], - }, -}; - -module.exports = { - Gledopto, -}; diff --git a/server/services/zigbee2mqtt/model/HEIMAN.js b/server/services/zigbee2mqtt/model/HEIMAN.js deleted file mode 100644 index 544dfdf96b..0000000000 --- a/server/services/zigbee2mqtt/model/HEIMAN.js +++ /dev/null @@ -1,27 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * HEIMAN managed models. - */ -const HEIMAN = { - brand: 'HEIMAN', - models: { - 'HS1CA-M': [features.smoke], - HS3MS: [features.motion], - HS2SK: [features.switch, features.power], - HS1SA: [features.smoke], - HS3SA: [features.smoke], - HS3CG: [features.smoke], - 'HS1DS/HS3DS': [features.door], - 'HEIMAN-M1': [features.door], - 'HS1DS-E': [features.door], - 'HS1WL/HS3WL': [features.water], - 'HS1-WL-E': [features.water], - 'HS1RC-M': [features.switch_sensor], - 'HS1CA-E': [features.smoke], - }, -}; - -module.exports = { - HEIMAN, -}; diff --git a/server/services/zigbee2mqtt/model/HamptonBay.js b/server/services/zigbee2mqtt/model/HamptonBay.js deleted file mode 100644 index 97a53a86e5..0000000000 --- a/server/services/zigbee2mqtt/model/HamptonBay.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Hampton Bay managed models. - */ -const HamptonBay = { - brand: 'Hampton Bay', - models: { - '99432': [features.switch_sensor], - }, -}; - -module.exports = { - HamptonBay, -}; diff --git a/server/services/zigbee2mqtt/model/Hive.js b/server/services/zigbee2mqtt/model/Hive.js deleted file mode 100644 index a3a7810579..0000000000 --- a/server/services/zigbee2mqtt/model/Hive.js +++ /dev/null @@ -1,19 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Hive managed models. - */ -const Hive = { - brand: 'Hive', - models: { - HALIGHTDIMWWE27: [features.light, features.brightness], - HALIGHTDIMWWB22: [features.light, features.brightness], - '1613V': [features.switch, features.power], - 'HV-GSCXZB269': [features.light, features.brightness, features.color_temperature], - 'HV-GSCXZB279_HV-GSCXZB229': [features.light, features.brightness, features.color_temperature], - }, -}; - -module.exports = { - Hive, -}; diff --git a/server/services/zigbee2mqtt/model/Honyar.js b/server/services/zigbee2mqtt/model/Honyar.js deleted file mode 100644 index ca031e265a..0000000000 --- a/server/services/zigbee2mqtt/model/Honyar.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Honyar managed models. - */ -const Honyar = { - brand: 'Honyar', - models: { - U86K31ND6: [features.switch], - }, -}; - -module.exports = { - Honyar, -}; diff --git a/server/services/zigbee2mqtt/model/IKEA.js b/server/services/zigbee2mqtt/model/IKEA.js deleted file mode 100644 index a152dcb20a..0000000000 --- a/server/services/zigbee2mqtt/model/IKEA.js +++ /dev/null @@ -1,43 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * IKEA managed models. - */ -const IKEA = { - brand: 'IKEA', - models: { - LED1545G12: [features.light, features.brightness, features.color_temperature], - LED1546G12: [features.light, features.brightness, features.color_temperature], - LED1623G12: [features.light, features.brightness], - LED1537R6: [features.light, features.brightness, features.color_temperature], - LED1650R5: [features.light, features.brightness], - LED1536G5: [features.light, features.brightness, features.color_temperature], - LED1733G7: [features.light, features.brightness, features.color_temperature], - LED1622G12: [features.light, features.brightness], - LED1624G9: [features.light, features.brightness, features.color], - LED1649C5: [features.light, features.brightness], - LED1934G3: [features.light, features.brightness], - LED1732G11: [features.light, features.brightness, features.color_temperature], - LED1924G9: [features.light, features.brightness, features.color_temperature, features.color], - 'ICTC-G-1': [features.brightness, features.switch_sensor], - 'ICPSHC24-10EU-IL-1': [features.light, features.brightness], - 'ICPSHC24-30EU-IL-1': [features.light, features.brightness], - L1527: [features.light, features.brightness, features.color_temperature], - L1529: [features.light, features.brightness, features.color_temperature], - L1528: [features.light, features.brightness, features.color_temperature], - L1531: [features.light, features.brightness, features.color_temperature], - 'E1603/E1702': [features.switch], - 'E1524/E1810': [features.switch_sensor], - E1743: [features.button], - 'E1525/E1745': [features.motion], - 'E1603/E1702/E1708': [features.switch], - // E1746: [], // Signal repeater - 'E2001/E2002': [features.button], - LED1836G9: [features.light, features.brightness], - LED1837R5: [features.light], - }, -}; - -module.exports = { - IKEA, -}; diff --git a/server/services/zigbee2mqtt/model/Iluminize.js b/server/services/zigbee2mqtt/model/Iluminize.js deleted file mode 100644 index eb46c1aac3..0000000000 --- a/server/services/zigbee2mqtt/model/Iluminize.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Iluminize managed models. - */ -const Iluminize = { - brand: 'Iluminize', - models: { - '511.10': [features.light, features.brightness], - }, -}; - -module.exports = { - Iluminize, -}; diff --git a/server/services/zigbee2mqtt/model/Immax.js b/server/services/zigbee2mqtt/model/Immax.js deleted file mode 100644 index 43930a74d0..0000000000 --- a/server/services/zigbee2mqtt/model/Immax.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Immax managed models. - */ -const Immax = { - brand: 'Immax', - models: { - 'IM-Z3.0-DIM': [features.light, features.brightness], - }, -}; - -module.exports = { - Immax, -}; diff --git a/server/services/zigbee2mqtt/model/Innr.js b/server/services/zigbee2mqtt/model/Innr.js deleted file mode 100644 index 2971643d24..0000000000 --- a/server/services/zigbee2mqtt/model/Innr.js +++ /dev/null @@ -1,42 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Innr managed models. - */ -const Innr = { - brand: 'Innr', - models: { - 'RB 185 C': [features.light, features.brightness, features.color_temperature, features.color], - 'BY 185 C': [features.light, features.brightness, features.color_temperature, features.color], - 'RB 250 C': [features.light, features.brightness, features.color_temperature, features.color], - 'RB 265': [features.light, features.brightness], - 'RB 278 T': [features.light, features.brightness], - 'RB 285 C': [features.light, features.brightness, features.color_temperature, features.color], - 'BY 285 C': [features.light, features.brightness, features.color_temperature, features.color], - 'RB 165': [features.light, features.brightness], - 'RB 175 W': [features.light, features.brightness], - 'RB 178 T': [features.light, features.brightness, features.color_temperature], - 'RS 122': [features.light, features.brightness], - 'RS 125': [features.light, features.brightness], - 'RS 225': [features.light, features.brightness], - 'RS 128 T': [features.light, features.brightness, features.color_temperature], - 'RS 228 T': [features.light, features.brightness, features.color_temperature], - 'RB 145': [features.light, features.brightness], - 'RB 245': [features.light, features.brightness], - 'RB 248 T': [features.light, features.brightness, features.color_temperature], - 'BY 165': [features.light, features.brightness], - 'PL 110': [features.light, features.brightness], - 'ST 110': [features.light, features.brightness], - 'UC 110': [features.light, features.brightness], - 'DL 110 N': [features.light, features.brightness], - 'DL 110 W': [features.light, features.brightness], - 'SL 110 N': [features.light, features.brightness], - 'SL 110 M': [features.light, features.brightness], - 'SL 110 W': [features.light, features.brightness], - 'SP 120': [features.switch, features.power], - }, -}; - -module.exports = { - Innr, -}; diff --git a/server/services/zigbee2mqtt/model/Iris.js b/server/services/zigbee2mqtt/model/Iris.js deleted file mode 100644 index b2d92496d6..0000000000 --- a/server/services/zigbee2mqtt/model/Iris.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Iris managed models. - */ -const Iris = { - brand: 'Iris', - models: { - '3210-L': [features.switch], - '3326-L': [features.temperature, features.motion], - '3320-L': [features.door], - }, -}; - -module.exports = { - Iris, -}; diff --git a/server/services/zigbee2mqtt/model/JIAWEN.js b/server/services/zigbee2mqtt/model/JIAWEN.js deleted file mode 100644 index 684e31de96..0000000000 --- a/server/services/zigbee2mqtt/model/JIAWEN.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * JIAWEN managed models. - */ -const JIAWEN = { - brand: 'JIAWEN', - models: { - K2RGBW01: [features.light, features.brightness, features.color_temperature, features.color], - }, -}; - -module.exports = { - JIAWEN, -}; diff --git a/server/services/zigbee2mqtt/model/KeenHome.js b/server/services/zigbee2mqtt/model/KeenHome.js deleted file mode 100644 index 1871ea801f..0000000000 --- a/server/services/zigbee2mqtt/model/KeenHome.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Keen Home managed models. - */ -const KeenHome = { - brand: 'Keen Home', - models: { - SV01: [features.door, features.temperature, features.pressure], - SV02: [features.door, features.temperature, features.pressure], - }, -}; - -module.exports = { - KeenHome, -}; diff --git a/server/services/zigbee2mqtt/model/KsentryElectronics.js b/server/services/zigbee2mqtt/model/KsentryElectronics.js deleted file mode 100644 index a4222f3f6b..0000000000 --- a/server/services/zigbee2mqtt/model/KsentryElectronics.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Ksentry Electronics managed models. - */ -const KsentryElectronics = { - brand: 'Ksentry Electronics', - models: { - 'KS-SM001': [features.switch], - }, -}; - -module.exports = { - KsentryElectronics, -}; diff --git a/server/services/zigbee2mqtt/model/Leedarson.js b/server/services/zigbee2mqtt/model/Leedarson.js deleted file mode 100644 index 8f736fdf9c..0000000000 --- a/server/services/zigbee2mqtt/model/Leedarson.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Leedarson managed models. - */ -const Leedarson = { - brand: 'Leedarson', - models: { - ZM350STW1TCF: [features.light, features.brightness, features.color_temperature], - M350STW1: [features.light, features.brightness], - 'A806S-Q1R': [features.light, features.brightness], - }, -}; - -module.exports = { - Leedarson, -}; diff --git a/server/services/zigbee2mqtt/model/Lidl.js b/server/services/zigbee2mqtt/model/Lidl.js deleted file mode 100644 index 5422bbc598..0000000000 --- a/server/services/zigbee2mqtt/model/Lidl.js +++ /dev/null @@ -1,25 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Lidl managed models. - */ -const Lidl = { - brand: 'Lidl', - models: { - HG06337: [features.switch], - HG06104A: [features.light, features.brightness, features.color_temperature, features.color], - HG06492C: [features.light, features.brightness, features.color_temperature], - HG06492B: [features.light, features.brightness, features.color_temperature], - HG06492A: [features.light, features.brightness, features.color_temperature], - HG06106C: [features.light, features.brightness, features.color_temperature, features.color], - HG06106A: [features.light, features.brightness, features.color_temperature, features.color], - HG06106B: [features.light, features.brightness, features.color_temperature, features.color], - '14147206L': [features.light, features.brightness, features.color_temperature, features.color], - '14148906L': [features.light, features.brightness, features.color_temperature, features.color], - '14149505L/14149506L': [features.light, features.brightness, features.color_temperature, features.color], - }, -}; - -module.exports = { - Lidl, -}; diff --git a/server/services/zigbee2mqtt/model/LivingWise.js b/server/services/zigbee2mqtt/model/LivingWise.js deleted file mode 100644 index 3711faa0ff..0000000000 --- a/server/services/zigbee2mqtt/model/LivingWise.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * LivingWise managed models. - */ -const LivingWise = { - brand: 'LivingWise', - models: { - 'LVS-ZB500D': [features.light, features.brightness], - 'LVS-SM10ZW': [features.door], - }, -}; - -module.exports = { - LivingWise, -}; diff --git a/server/services/zigbee2mqtt/model/Livolo.js b/server/services/zigbee2mqtt/model/Livolo.js deleted file mode 100644 index ea583aeaa3..0000000000 --- a/server/services/zigbee2mqtt/model/Livolo.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Livolo managed models. - */ -const Livolo = { - brand: 'Livolo', - models: { - TI0001: [features.switch_sensor], - }, -}; - -module.exports = { - Livolo, -}; diff --git a/server/services/zigbee2mqtt/model/Lonsonho.js b/server/services/zigbee2mqtt/model/Lonsonho.js deleted file mode 100644 index f20c3d7852..0000000000 --- a/server/services/zigbee2mqtt/model/Lonsonho.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Leedarson managed models. - */ -const Lonsonho = { - brand: 'Lonsonho', - models: { - 'QS-Zigbee-D02-TRIAC-L': [features.light, features.brightness], - 'QS-Zigbee-D02-TRIAC-LN': [features.light, features.brightness], - }, -}; - -module.exports = { - Lonsonho, -}; diff --git a/server/services/zigbee2mqtt/model/Lupus.js b/server/services/zigbee2mqtt/model/Lupus.js deleted file mode 100644 index 8b87c0df0a..0000000000 --- a/server/services/zigbee2mqtt/model/Lupus.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Lupus managed models. - */ -const Lupus = { - brand: 'Lupus', - models: { - '12031': [features.switch], // curtain / shutter - '12050': [features.switch, features.power], - }, -}; - -module.exports = { - Lupus, -}; diff --git a/server/services/zigbee2mqtt/model/Meazon.js b/server/services/zigbee2mqtt/model/Meazon.js deleted file mode 100644 index 72e4553854..0000000000 --- a/server/services/zigbee2mqtt/model/Meazon.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Meazon managed models. - */ -const Meazon = { - brand: 'Meazon', - models: { - MEAZON_BIZY_PLUG: [features.switch, features.power, features.temperature], - MEAZON_DINRAIL: [features.switch, features.power, features.temperature], - }, -}; - -module.exports = { - Meazon, -}; diff --git a/server/services/zigbee2mqtt/model/MullerLicht.js b/server/services/zigbee2mqtt/model/MullerLicht.js deleted file mode 100644 index a413c28ec8..0000000000 --- a/server/services/zigbee2mqtt/model/MullerLicht.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Müller Licht managed models. - */ -const MullerLicht = { - brand: 'Müller Licht', - models: { - '404000/404005/404012': [features.light, features.brightness, features.color_temperature, features.color], - '404006/404008/404004': [features.light, features.brightness, features.color_temperature], - 'MLI-404011': [features.switch_sensor], - }, -}; - -module.exports = { - MullerLicht, -}; diff --git a/server/services/zigbee2mqtt/model/NET2GRID.js b/server/services/zigbee2mqtt/model/NET2GRID.js deleted file mode 100644 index 5e8c8d30d0..0000000000 --- a/server/services/zigbee2mqtt/model/NET2GRID.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * NET2GRID managed models. - */ -const NET2GRID = { - brand: 'NET2GRID', - models: { - 'N2G-SP': [features.switch, features.power], - }, -}; - -module.exports = { - NET2GRID, -}; diff --git a/server/services/zigbee2mqtt/model/Nanoleaf.js b/server/services/zigbee2mqtt/model/Nanoleaf.js deleted file mode 100644 index 9f0e01956a..0000000000 --- a/server/services/zigbee2mqtt/model/Nanoleaf.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Nanoleaf managed models. - */ -const Nanoleaf = { - brand: 'Nanoleaf', - models: { - 'NL08-0800': [features.light, features.brightness], - }, -}; - -module.exports = { - Nanoleaf, -}; diff --git a/server/services/zigbee2mqtt/model/Netvox.js b/server/services/zigbee2mqtt/model/Netvox.js deleted file mode 100644 index aeb68b5707..0000000000 --- a/server/services/zigbee2mqtt/model/Netvox.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Netvox managed models. - */ -const Netvox = { - brand: 'Netvox', - models: { - Z809A: [features.switch, features.power], - }, -}; - -module.exports = { - Netvox, -}; diff --git a/server/services/zigbee2mqtt/model/NinjaBlocks.js b/server/services/zigbee2mqtt/model/NinjaBlocks.js deleted file mode 100644 index b877835972..0000000000 --- a/server/services/zigbee2mqtt/model/NinjaBlocks.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Ninja Blocks managed models. - */ -const NinjaBlocks = { - brand: 'Ninja Blocks', - models: { - Z809AF: [features.switch, features.power], - }, -}; - -module.exports = { - NinjaBlocks, -}; diff --git a/server/services/zigbee2mqtt/model/Nue_3A.js b/server/services/zigbee2mqtt/model/Nue_3A.js deleted file mode 100644 index ce938befd1..0000000000 --- a/server/services/zigbee2mqtt/model/Nue_3A.js +++ /dev/null @@ -1,31 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Nue / 3A managed models. - */ -const Nue3A = { - brand: 'Nue / 3A', - models: { - 'HGZB-1S': [features.switch, features.switch_sensor], - 'HGZB-02S': [features.switch, features.switch_sensor], - 'HGZB-045': [features.switch, features.switch_sensor], - 'LXZB-02A': [features.light, features.brightness], - 'HGZB-43': [features.switch], - 'HGZB-043': [features.switch], - 'HGZB-04D': [features.light, features.brightness], - 'HGZB-042': [features.switch], - 'HGZB-42': [features.switch], - 'HGZB-01A/02A': [features.switch], - 'HGZB-41': [features.switch], - 'MG-AUWS01': [features.switch], - 'HGZB-01A': [features.light, features.brightness], - 'XY12S-15': [features.light, features.brightness, features.color_temperature, features.color], - 'HGZB-02A': [features.light, features.brightness], - 'HGZB-42-UK / HGZB-41': [features.switch], - 'HGZB-06A': [features.light, features.brightness, features.color_temperature, features.color], - }, -}; - -module.exports = { - Nue3A, -}; diff --git a/server/services/zigbee2mqtt/model/Nyce.js b/server/services/zigbee2mqtt/model/Nyce.js deleted file mode 100644 index 1a55cb52de..0000000000 --- a/server/services/zigbee2mqtt/model/Nyce.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Nyce managed models. - */ -const Nyce = { - brand: 'Nyce', - models: { - 'NCZ-3011-HA': [features.temperature, features.motion, features.humidity], - 'NCZ-3043-HA': [features.temperature, features.motion], - 'NCZ-3041-HA': [features.temperature, features.motion], - 'NCZ-3045-HA': [features.temperature, features.motion], - }, -}; - -module.exports = { - Nyce, -}; diff --git a/server/services/zigbee2mqtt/model/ORVIBO.js b/server/services/zigbee2mqtt/model/ORVIBO.js deleted file mode 100644 index 840b0e29ad..0000000000 --- a/server/services/zigbee2mqtt/model/ORVIBO.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * ORVIBO managed models. - */ -const ORVIBO = { - brand: 'ORVIBO', - models: { - SN10ZW: [features.motion], - }, -}; - -module.exports = { - ORVIBO, -}; diff --git a/server/services/zigbee2mqtt/model/OSRAM.js b/server/services/zigbee2mqtt/model/OSRAM.js deleted file mode 100644 index c843ec42cd..0000000000 --- a/server/services/zigbee2mqtt/model/OSRAM.js +++ /dev/null @@ -1,37 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * OSRAM managed models. - */ -const OSRAM = { - brand: 'OSRAM', - models: { - '4058075816718': [features.light, features.brightness, features.color_temperature, features.color], - AA69697: [features.light, features.brightness, features.color_temperature, features.color], - AC03645: [features.light, features.brightness, features.color_temperature, features.color], - AC03642: [features.light, features.brightness, features.color_temperature], - AC03647: [features.light, features.brightness, features.color_temperature, features.color], - AA70155: [features.light, features.brightness, features.color_temperature], - AA68199: [features.light, features.brightness, features.color_temperature], - AB32840: [features.light, features.brightness, features.color_temperature], - '4058075816794': [features.light, features.brightness, features.color_temperature], - AC03641: [features.light, features.brightness], - '4052899926158': [features.light, features.brightness], - AB401130055: [features.light, features.brightness, features.color_temperature], - AB3257001NJ: [features.switch], - '4052899926110': [features.light, features.brightness, features.color_temperature, features.color], - '4058075036185': [features.light, features.brightness, features.color_temperature, features.color], - '4058075036147': [features.light, features.brightness, features.color_temperature, features.color], - AC0363900NJ: [features.light, features.brightness, features.color_temperature, features.color], - AB35996: [features.light, features.brightness, features.color_temperature, features.color], - AC08562: [features.light, features.brightness], - AC01353010G: [features.temperature, features.motion], - AC03648: [features.light, features.brightness, features.color_temperature], - AC0251100NJ: [features.switch_sensor], - 'ST8AU-CON': [features.light, features.brightness], - }, -}; - -module.exports = { - OSRAM, -}; diff --git a/server/services/zigbee2mqtt/model/Oujiabao.js b/server/services/zigbee2mqtt/model/Oujiabao.js deleted file mode 100644 index 5a39c2f9f0..0000000000 --- a/server/services/zigbee2mqtt/model/Oujiabao.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Oujiabao managed models. - */ -const Oujiabao = { - brand: 'Oujiabao', - models: { - 'CR701-YZ': [features.smoke, features.smoke], - }, -}; - -module.exports = { - Oujiabao, -}; diff --git a/server/services/zigbee2mqtt/model/PaulNeuhaus.js b/server/services/zigbee2mqtt/model/PaulNeuhaus.js deleted file mode 100644 index 2320f8a3ee..0000000000 --- a/server/services/zigbee2mqtt/model/PaulNeuhaus.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Paul Neuhaus managed models. - */ -const PaulNeuhaus = { - brand: 'Paul Neuhaus', - models: { - '100.424.11': [features.light, features.brightness, features.color_temperature], - '100.110.39': [features.light, features.brightness, features.color_temperature, features.color], - '100.425.90': [features.switch], - }, -}; - -module.exports = { - PaulNeuhaus, -}; diff --git a/server/services/zigbee2mqtt/model/Paulmann.js b/server/services/zigbee2mqtt/model/Paulmann.js deleted file mode 100644 index bf03bf8a7a..0000000000 --- a/server/services/zigbee2mqtt/model/Paulmann.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Paulmann managed models. - */ -const Paulmann = { - brand: 'Paulmann', - models: { - '50043': [features.switch], - '50045': [features.light, features.brightness], - '50049': [features.light, features.brightness, features.color_temperature, features.color], - '50064': [features.light, features.brightness, features.color_temperature], - }, -}; - -module.exports = { - Paulmann, -}; diff --git a/server/services/zigbee2mqtt/model/Philips.js b/server/services/zigbee2mqtt/model/Philips.js deleted file mode 100644 index ed0127c652..0000000000 --- a/server/services/zigbee2mqtt/model/Philips.js +++ /dev/null @@ -1,51 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Philips managed models. - */ -const Philips = { - brand: 'Philips', - models: { - '7299760PH': [features.light, features.brightness, features.color], - '7146060PH': [features.light, features.brightness, features.color_temperature, features.color], - '4090531P7': [features.light, features.brightness, features.color_temperature, features.color], - '433714': [features.light, features.brightness], - '8718696449691': [features.light, features.brightness], - '9290018195': [features.light, features.brightness], - '7299355PH': [features.light, features.brightness, features.color], - '915005106701': [features.light, features.brightness, features.color_temperature, features.color], - '9290012573A': [features.light, features.brightness, features.color_temperature, features.color], - '9290002579A': [features.light, features.brightness, features.color_temperature, features.color], - '8718696485880': [features.light, features.brightness, features.color_temperature, features.color], - '915005733701': [features.light, features.brightness, features.color_temperature, features.color], - '464800': [features.light, features.brightness, features.color_temperature], - '8718696695203': [features.light, features.brightness, features.color_temperature], - '8718696598283': [features.light, features.brightness, features.color_temperature], - '9290011998B': [features.light, features.brightness, features.color_temperature], - '8718696548738': [features.light, features.brightness, features.color_temperature], - '4090130P7': [features.light, features.brightness, features.color_temperature, features.color], - '3261030P7': [features.light, features.brightness, features.color_temperature], - '3261331P7': [features.light, features.brightness, features.color_temperature], - '4096730U7': [features.light, features.brightness, features.color_temperature], - '3216131P5': [features.light, features.brightness, features.color_temperature], - '3216331P5': [features.light, features.brightness, features.color_temperature], - '3216431P5': [features.light, features.brightness, features.color_temperature], - '4033930P7': [features.light, features.brightness, features.color_temperature], - '9290011370B': [features.light, features.brightness], - '046677476816': [features.light, features.brightness], - '7199960PH': [features.light, features.brightness, features.color], - '324131092621': [features.switch_sensor], - '9290012607': [features.temperature, features.motion, features.illuminance], - '9290019758': [features.temperature, features.motion, features.illuminance], - '7099860PH': [features.light, features.brightness, features.color], - '3216231P5': [features.light, features.brightness, features.color_temperature], - '8718696170625': [features.light, features.brightness], - '8718699673147': [features.light, features.brightness], - '9290022166': [features.light, features.brightness, features.color_temperature], // color xy - '929002241201': [features.light, features.brightness], - }, -}; - -module.exports = { - Philips, -}; diff --git a/server/services/zigbee2mqtt/model/RGBGenie.js b/server/services/zigbee2mqtt/model/RGBGenie.js deleted file mode 100644 index 77418405b1..0000000000 --- a/server/services/zigbee2mqtt/model/RGBGenie.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * RGBGenie managed models. - */ -const RGBGenie = { - brand: 'RGB Genie', - models: { - 'ZGRC-KEY-013': [features.switch_sensor], - }, -}; - -module.exports = { - RGBGenie, -}; diff --git a/server/services/zigbee2mqtt/model/ROBB.js b/server/services/zigbee2mqtt/model/ROBB.js deleted file mode 100644 index 7ddc5b531f..0000000000 --- a/server/services/zigbee2mqtt/model/ROBB.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * ROBB managed models. - */ -const ROBB = { - brand: 'ROBB', - models: { - 'ROB_200-004-0': [features.light, features.brightness], - }, -}; - -module.exports = { - ROBB, -}; diff --git a/server/services/zigbee2mqtt/model/SONOFF.js b/server/services/zigbee2mqtt/model/SONOFF.js deleted file mode 100644 index 616448e8ae..0000000000 --- a/server/services/zigbee2mqtt/model/SONOFF.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * SONOFF managed models. - */ -const SONOFF = { - brand: 'SONOFF', - models: { - 'SNZB-01': [features.button], - 'SNZB-02': [features.temperature, features.humidity], - 'SNZB-03': [features.motion], - 'SNZB-04': [features.door], - }, -}; - -module.exports = { - SONOFF, -}; diff --git a/server/services/zigbee2mqtt/model/Salus.js b/server/services/zigbee2mqtt/model/Salus.js deleted file mode 100644 index 8584e79022..0000000000 --- a/server/services/zigbee2mqtt/model/Salus.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Salus managed models. - */ -const Salus = { - brand: 'Salus', - models: { - SP600: [features.switch, features.power], - }, -}; - -module.exports = { - Salus, -}; diff --git a/server/services/zigbee2mqtt/model/Securifi.js b/server/services/zigbee2mqtt/model/Securifi.js deleted file mode 100644 index ad80ea8c96..0000000000 --- a/server/services/zigbee2mqtt/model/Securifi.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Securifi managed models. - */ -const Securifi = { - brand: 'Securifi', - models: { - 'PP-WHT-US': [features.switch, features.power, features.current, features.voltage], - }, -}; - -module.exports = { - Securifi, -}; diff --git a/server/services/zigbee2mqtt/model/Sengled.js b/server/services/zigbee2mqtt/model/Sengled.js deleted file mode 100644 index 0cf92ae916..0000000000 --- a/server/services/zigbee2mqtt/model/Sengled.js +++ /dev/null @@ -1,21 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Sengled managed models. - */ -const Sengled = { - brand: 'Sengled', - models: { - 'E11-G13': [features.light, features.brightness], - 'E11-G23/E11-G33': [features.light, features.brightness], - 'Z01-CIA19NAE26': [features.light, features.brightness], - 'Z01-A19NAE26': [features.light, features.brightness, features.color_temperature], - 'E11-N1EA': [features.light, features.brightness, features.color_temperature, features.color], - 'E12-N14': [features.light, features.brightness], - E1ACA4ABE38A: [features.light, features.brightness], - }, -}; - -module.exports = { - Sengled, -}; diff --git a/server/services/zigbee2mqtt/model/Sercomm.js b/server/services/zigbee2mqtt/model/Sercomm.js deleted file mode 100644 index 4401b382e7..0000000000 --- a/server/services/zigbee2mqtt/model/Sercomm.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Sercomm managed models. - */ -const Sercomm = { - brand: 'Sercomm', - models: { - 'SZ-ESW01-AU': [features.switch, features.power], - }, -}; - -module.exports = { - Sercomm, -}; diff --git a/server/services/zigbee2mqtt/model/ShenzhenHoma.js b/server/services/zigbee2mqtt/model/ShenzhenHoma.js deleted file mode 100644 index ffb9086be8..0000000000 --- a/server/services/zigbee2mqtt/model/ShenzhenHoma.js +++ /dev/null @@ -1,17 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Shenzhen Homa managed models. - */ -const ShenzhenHoma = { - brand: 'Shenzhen Homa', - models: { - 'HLD812-Z-SC': [features.light, features.brightness], - 'HLC610-Z': [features.light, features.brightness], - 'HLC821-Z-SC': [features.light, features.brightness], - }, -}; - -module.exports = { - ShenzhenHoma, -}; diff --git a/server/services/zigbee2mqtt/model/SmartHomePty.js b/server/services/zigbee2mqtt/model/SmartHomePty.js deleted file mode 100644 index d21d080d60..0000000000 --- a/server/services/zigbee2mqtt/model/SmartHomePty.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Smart Home Pty managed models. - */ -const SmartHomePty = { - brand: 'Smart Home Pty', - models: { - 'HGZB-07A': [features.switch, features.color_temperature], - 'HGZB-20-DE': [features.switch], - }, -}; - -module.exports = { - SmartHomePty, -}; diff --git a/server/services/zigbee2mqtt/model/SmartThings.js b/server/services/zigbee2mqtt/model/SmartThings.js deleted file mode 100644 index 3ac1f0e763..0000000000 --- a/server/services/zigbee2mqtt/model/SmartThings.js +++ /dev/null @@ -1,29 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * SmartThings managed models. - */ -const SmartThings = { - brand: 'SmartThings', - models: { - 'STSS-MULT-001': [features.door], - 'STS-PRS-251': [features.motion], - '3325-S': [features.temperature, features.motion], - '3321-S': [features.temperature, features.door], - 'IM6001-OTP05': [features.switch], - 'IM6001-MTP01': [features.temperature, features.motion], - 'STS-IRM-250': [features.temperature, features.motion], - '3305-S': [features.temperature, features.motion], - '3300-S': [features.temperature, features.door], - 'F-MLT-US-2': [features.temperature, features.door], - 'IM6001-MPP01': [features.temperature, features.door], - '3310-S': [features.temperature], - '3315-S': [features.temperature, features.water], - '3315-G': [features.temperature, features.water], - 'IM6001-BTP01': [features.switch_sensor, features.temperature], - }, -}; - -module.exports = { - SmartThings, -}; diff --git a/server/services/zigbee2mqtt/model/Stelpro.js b/server/services/zigbee2mqtt/model/Stelpro.js deleted file mode 100644 index cc8de5bfcb..0000000000 --- a/server/services/zigbee2mqtt/model/Stelpro.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Stelpro managed models. - */ -const Stelpro = { - brand: 'Stelpro', - models: { - ST218: [features.temperature], - }, -}; - -module.exports = { - Stelpro, -}; diff --git a/server/services/zigbee2mqtt/model/Sunricher.js b/server/services/zigbee2mqtt/model/Sunricher.js deleted file mode 100644 index 93cae3b753..0000000000 --- a/server/services/zigbee2mqtt/model/Sunricher.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Sunricher managed models. - */ -const Sunricher = { - brand: 'Sunricher', - models: { - 'ZG9101SAC-HP': [features.light, features.brightness], - }, -}; - -module.exports = { - Sunricher, -}; diff --git a/server/services/zigbee2mqtt/model/Swann.js b/server/services/zigbee2mqtt/model/Swann.js deleted file mode 100644 index 9535e5f352..0000000000 --- a/server/services/zigbee2mqtt/model/Swann.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Swann managed models. - */ -const Swann = { - brand: 'Swann', - models: { - 'SWO-KEF1PA': [features.switch_sensor], - 'SWO-WDS1PA': [features.door], - }, -}; - -module.exports = { - Swann, -}; diff --git a/server/services/zigbee2mqtt/model/Sylvania.js b/server/services/zigbee2mqtt/model/Sylvania.js deleted file mode 100644 index 311cd44b6b..0000000000 --- a/server/services/zigbee2mqtt/model/Sylvania.js +++ /dev/null @@ -1,25 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Sylvania managed models. - */ -const Sylvania = { - brand: 'Sylvania', - models: { - '73742': [features.light, features.brightness, features.color_temperature], - '73740': [features.light, features.brightness, features.color_temperature], - '73739': [features.light, features.brightness, features.color_temperature, features.color], - '73693': [features.light, features.brightness, features.color_temperature, features.color], - '74283': [features.light, features.brightness], - '74696': [features.light, features.brightness], - '72922-A': [features.switch], - '71831': [features.light, features.brightness, features.color_temperature], - '74282': [features.light, features.brightness, features.color_temperature], - LTFY004: [features.light, features.brightness, features.color], - '74580': [features.light, features.brightness], - }, -}; - -module.exports = { - Sylvania, -}; diff --git a/server/services/zigbee2mqtt/model/TUYATEC.js b/server/services/zigbee2mqtt/model/TUYATEC.js deleted file mode 100644 index a11a53cb20..0000000000 --- a/server/services/zigbee2mqtt/model/TUYATEC.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * TUYATEC managed models. - */ -const TUYATEC = { - brand: 'TUYATEC', - models: { - RH3040: [features.motion], - }, -}; - -module.exports = { - TUYATEC, -}; diff --git a/server/services/zigbee2mqtt/model/ThirdReality.js b/server/services/zigbee2mqtt/model/ThirdReality.js deleted file mode 100644 index 6757d33e8e..0000000000 --- a/server/services/zigbee2mqtt/model/ThirdReality.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Third Reality managed models. - */ -const ThirdReality = { - brand: 'Third Reality', - models: { - '3RSS008Z': [features.switch_sensor], - }, -}; - -module.exports = { - ThirdReality, -}; diff --git a/server/services/zigbee2mqtt/model/Trust.js b/server/services/zigbee2mqtt/model/Trust.js deleted file mode 100644 index c174821116..0000000000 --- a/server/services/zigbee2mqtt/model/Trust.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Trust managed models. - */ -const Trust = { - brand: 'Trust', - models: { - 'ZYCT-202': [features.switch_sensor], - 'ZLED-2709': [features.light, features.brightness], - 'ZPIR-8000': [features.motion], - 'ZCTS-808': [features.door], - }, -}; - -module.exports = { - Trust, -}; diff --git a/server/services/zigbee2mqtt/model/TuYa.js b/server/services/zigbee2mqtt/model/TuYa.js deleted file mode 100644 index d64c32b384..0000000000 --- a/server/services/zigbee2mqtt/model/TuYa.js +++ /dev/null @@ -1,21 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * TuYa managed models. - */ -const TuYa = { - brand: 'TuYa', - models: { - TS0121_plug: [features.switch, features.power, features.current, features.voltage, features.energy], - TS0201: [features.temperature, features.humidity, features.voltage], - TS0011: [features.switch], - TS0601_air_quality_sensor: [features.temperature, features.humidity, features.co2], - TT001ZAV20: [features.temperature, features.humidity], - SNTZ007: [features.door], - TS0503B: [features.light, features.brightness, features.color], - }, -}; - -module.exports = { - TuYa, -}; diff --git a/server/services/zigbee2mqtt/model/Visonic.js b/server/services/zigbee2mqtt/model/Visonic.js deleted file mode 100644 index c5d1b70eef..0000000000 --- a/server/services/zigbee2mqtt/model/Visonic.js +++ /dev/null @@ -1,16 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Visonic managed models. - */ -const Visonic = { - brand: 'Visonic', - models: { - 'MCT-350 SMA': [features.door], - 'MCT-340 E': [features.door], - }, -}; - -module.exports = { - Visonic, -}; diff --git a/server/services/zigbee2mqtt/model/Xiaomi.js b/server/services/zigbee2mqtt/model/Xiaomi.js deleted file mode 100644 index 2f721d9da6..0000000000 --- a/server/services/zigbee2mqtt/model/Xiaomi.js +++ /dev/null @@ -1,45 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Xiaomi managed models. - */ -const Xiaomi = { - brand: 'Xiaomi', - models: { - ZNLDP12LM: [features.light, features.brightness, features.color_temperature], - WXKG01LM: [features.button], - WXKG11LM: [features.button], - WXKG12LM: [features.button], - WXKG03LM_rev1: [features.button], - WXKG03LM_rev2: [features.button], - WXKG02LM_rev1: [features.button], - WXKG02LM_rev2: [features.button], - QBKG04LM: [features.switch], - QBKG11LM: [features.switch, features.power], - QBKG03LM: [features.switch_sensor], - QBKG12LM: [features.switch, features.power], - WSDCGQ01LM: [features.temperature, features.humidity], - WSDCGQ11LM: [features.temperature, features.humidity, features.pressure], - RTCGQ01LM: [features.motion], - RTCGQ11LM: [features.motion, features.illuminance, features.temperature, features.voltage], - MCCGQ01LM: [features.door], - MCCGQ11LM: [features.door, features.temperature], - SJCGQ11LM: [features.water], - MFKZQ01LM: [features.button], - ZNCZ02LM: [features.switch, features.power], - QBCZ11LM: [features.switch, features.power], - 'JTYJ-GD-01LM/BW': [features.smoke], - 'JTQJ-BF-01LM/BW': [features.smoke, features.gas_density], - A6121: [features.door], - DJT11LM: [features.button], - // ZNCLDJ11LM: [features.curtain] - LLKZMK11LM: [features.switch, features.power], - ZNMS12LM: [features.switch_sensor, features.door], - WXKG06LM: [features.button], - GZCGQ01LM: [features.illuminance, features.illuminance_lux], - }, -}; - -module.exports = { - Xiaomi, -}; diff --git a/server/services/zigbee2mqtt/model/Yale.js b/server/services/zigbee2mqtt/model/Yale.js deleted file mode 100644 index e27b60edcf..0000000000 --- a/server/services/zigbee2mqtt/model/Yale.js +++ /dev/null @@ -1,18 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * Yale managed models. - */ -const Yale = { - brand: 'Yale', - models: { - YRD426NRSC: [features.door], - YRD226HA2619: [features.door], - YRD256HA20BP: [features.door], - YMF40: [features.door], - }, -}; - -module.exports = { - Yale, -}; diff --git a/server/services/zigbee2mqtt/model/eCosy.js b/server/services/zigbee2mqtt/model/eCosy.js deleted file mode 100644 index 4ab856efbf..0000000000 --- a/server/services/zigbee2mqtt/model/eCosy.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * eCosy managed models. - */ -const eCosy = { - brand: 'eCosy', - models: { - '1TST-EU': [features.temperature, features.motion], // heating + schedule, - }, -}; - -module.exports = { - eCosy, -}; diff --git a/server/services/zigbee2mqtt/model/iCasa.js b/server/services/zigbee2mqtt/model/iCasa.js deleted file mode 100644 index ac52cfd3af..0000000000 --- a/server/services/zigbee2mqtt/model/iCasa.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * iCasa managed models. - */ -const iCasa = { - brand: 'iCasa', - models: { - 'ICZB-IW11D': [features.light, features.brightness], - }, -}; - -module.exports = { - iCasa, -}; diff --git a/server/services/zigbee2mqtt/model/ilux.js b/server/services/zigbee2mqtt/model/ilux.js deleted file mode 100644 index 8ebd330aa3..0000000000 --- a/server/services/zigbee2mqtt/model/ilux.js +++ /dev/null @@ -1,15 +0,0 @@ -const { features } = require('../utils/features'); - -/** - * ilux managed models. - */ -const ilux = { - brand: 'ilux', - models: { - '900008-WW': [features.light, features.brightness], - }, -}; - -module.exports = { - ilux, -}; diff --git a/server/services/zigbee2mqtt/model/index.js b/server/services/zigbee2mqtt/model/index.js deleted file mode 100644 index 1ec3392fa1..0000000000 --- a/server/services/zigbee2mqtt/model/index.js +++ /dev/null @@ -1,102 +0,0 @@ -const models = []; -models.push(require('./AduroSmart').AduroSmart); -models.push(require('./Airam').Airam); -models.push(require('./Anchor').Anchor); -models.push(require('./Belkin').Belkin); -models.push(require('./Bitron').Bitron); -models.push(require('./Blaupunkt').Blaupunkt); -models.push(require('./Bosch').Bosch); -models.push(require('./Calex').Calex); -models.push(require('./Centralite').Centralite); -models.push(require('./Climax').Climax); -models.push(require('./CommercialElectric').CommercialElectric); -models.push(require('./Danalock').Danalock); -models.push(require('./DresdenElektronik').DresdenElektronik); -models.push(require('./EDP').EDP); -models.push(require('./ELKO').ELKO); -models.push(require('./EcoSmart').EcoSmart); -models.push(require('./Eurotronic').Eurotronic); -models.push(require('./GE').GE); -models.push(require('./GMYSmartBulb').GMYSmartBulb); -models.push(require('./Gira').Gira); -models.push(require('./Gledopto').Gledopto); -models.push(require('./HEIMAN').HEIMAN); -models.push(require('./HamptonBay').HamptonBay); -models.push(require('./Hive').Hive); -models.push(require('./Honyar').Honyar); -models.push(require('./IKEA').IKEA); -models.push(require('./Iluminize').Iluminize); -models.push(require('./Immax').Immax); -models.push(require('./Innr').Innr); -models.push(require('./Iris').Iris); -models.push(require('./JIAWEN').JIAWEN); -models.push(require('./KeenHome').KeenHome); -models.push(require('./KsentryElectronics').KsentryElectronics); -models.push(require('./Leedarson').Leedarson); -models.push(require('./LivingWise').LivingWise); -models.push(require('./Livolo').Livolo); -models.push(require('./Lupus').Lupus); -models.push(require('./Meazon').Meazon); -models.push(require('./MullerLicht').MullerLicht); -models.push(require('./NET2GRID').NET2GRID); -models.push(require('./Nanoleaf').Nanoleaf); -models.push(require('./Netvox').Netvox); -models.push(require('./NinjaBlocks').NinjaBlocks); -models.push(require('./Nue_3A').Nue3A); -models.push(require('./Nyce').Nyce); -models.push(require('./ORVIBO').ORVIBO); -models.push(require('./OSRAM').OSRAM); -models.push(require('./Oujiabao').Oujiabao); -models.push(require('./PaulNeuhaus').PaulNeuhaus); -models.push(require('./Paulmann').Paulmann); -models.push(require('./Philips').Philips); -models.push(require('./RGBGenie').RGBGenie); -models.push(require('./ROBB').ROBB); -models.push(require('./Salus').Salus); -models.push(require('./Securifi').Securifi); -models.push(require('./Sengled').Sengled); -models.push(require('./Sercomm').Sercomm); -models.push(require('./ShenzhenHoma').ShenzhenHoma); -models.push(require('./SmartHomePty').SmartHomePty); -models.push(require('./SmartThings').SmartThings); -models.push(require('./SONOFF').SONOFF); -models.push(require('./Stelpro').Stelpro); -models.push(require('./Sunricher').Sunricher); -models.push(require('./Swann').Swann); -models.push(require('./Sylvania').Sylvania); -models.push(require('./TUYATEC').TUYATEC); -models.push(require('./ThirdReality').ThirdReality); -models.push(require('./Trust').Trust); -models.push(require('./Visonic').Visonic); -models.push(require('./Xiaomi').Xiaomi); -models.push(require('./Yale').Yale); -models.push(require('./eCosy').eCosy); -models.push(require('./iCasa').iCasa); -models.push(require('./ilux').ilux); -models.push(require('./TuYa').TuYa); -models.push(require('./Lonsonho').Lonsonho); -models.push(require('./Lidl').Lidl); -models.push(require('./Adeo').Adeo); - -/** - * @description Get features by model name. - * @param {string} modelName - Model name to find. - * @returns {Array} Related features. - * @example - * getFeaturesByModel('1TST-EU'); - */ -function getFeaturesByModel(modelName) { - const model = models.find((m) => { - return m.models[modelName]; - }); - - if (model) { - return model.models[modelName]; - } - - return []; -} - -module.exports = { - getFeaturesByModel, -}; diff --git a/server/services/zigbee2mqtt/utils/features.js b/server/services/zigbee2mqtt/utils/features.js deleted file mode 100644 index 884741a248..0000000000 --- a/server/services/zigbee2mqtt/utils/features.js +++ /dev/null @@ -1,272 +0,0 @@ -const { - DEVICE_FEATURE_CATEGORIES, - DEVICE_FEATURE_TYPES, - DEVICE_FEATURE_UNITS, -} = require('../../../../server/utils/constants'); - -const features = { - presence: { - category: DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Presence Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'presence', - }, - motion: { - category: DEVICE_FEATURE_CATEGORIES.MOTION_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Motion Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'occupancy', - }, - door: { - category: DEVICE_FEATURE_CATEGORIES.OPENING_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Opening Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'contact', - }, - water: { - category: DEVICE_FEATURE_CATEGORIES.LEAK_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Water Leak', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'water_leak', - }, - smoke: { - category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Smoke Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'smoke', - }, - battery: { - category: DEVICE_FEATURE_CATEGORIES.BATTERY, - type: DEVICE_FEATURE_TYPES.SENSOR.INTEGER, - unit: DEVICE_FEATURE_UNITS.PERCENT, - name: 'Battery', - read_only: true, - has_feedback: false, - min: 0, - max: 100, - zigbeeField: 'battery', - }, - illuminance: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - unit: DEVICE_FEATURE_UNITS.LUX, - name: 'Light Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1000, - zigbeeField: 'illuminance', - }, - illuminance_lux: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - unit: DEVICE_FEATURE_UNITS.LUX, - name: 'Light Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 100000, - zigbeeField: 'illuminance_lux', - }, - humidity: { - category: DEVICE_FEATURE_CATEGORIES.HUMIDITY_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - unit: DEVICE_FEATURE_UNITS.PERCENT, - name: 'Humidity', - read_only: true, - has_feedback: false, - min: 0, - max: 100, - zigbeeField: 'humidity', - }, - temperature: { - category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - unit: DEVICE_FEATURE_UNITS.CELSIUS, - name: 'Temperature', - read_only: true, - has_feedback: false, - min: -50, - max: 125, - zigbeeField: 'temperature', - }, - pressure: { - category: DEVICE_FEATURE_CATEGORIES.PRESSURE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - unit: DEVICE_FEATURE_UNITS.HECTO_PASCAL, - name: 'Pressure Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 10000, - zigbeeField: 'pressure', - }, - button: { - category: DEVICE_FEATURE_CATEGORIES.BUTTON, - type: DEVICE_FEATURE_TYPES.BUTTON.CLICK, - name: 'Button', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'action', - }, - switch: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Switch (On/Off)', - read_only: false, - has_feedback: true, - min: 0, - max: 1, - zigbeeField: 'state', - }, - switch_sensor: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Switch Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'state', - }, - power: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SWITCH.POWER, - unit: DEVICE_FEATURE_UNITS.WATT, - name: 'Power consumption', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'power', - }, - current: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SWITCH.CURRENT, - unit: DEVICE_FEATURE_UNITS.AMPERE, - name: 'Switch Current', - read_only: true, - has_feedback: false, - min: 0, - max: 1000, - zigbeeField: 'current', - }, - voltage: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SWITCH.VOLTAGE, - unit: DEVICE_FEATURE_UNITS.VOLT, - name: 'Switch Voltage', - read_only: true, - has_feedback: false, - min: 0, - max: 1000, - zigbeeField: 'voltage', - }, - energy: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SWITCH.ENERGY, - unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, - name: 'Energy consumption', - read_only: true, - has_feedback: false, - min: 0, - max: 10000, - zigbeeField: 'energy', - }, - light: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Light', - read_only: false, - has_feedback: true, - min: 0, - max: 1, - zigbeeField: 'state', - }, - brightness: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT, - type: DEVICE_FEATURE_TYPES.LIGHT.BRIGHTNESS, - name: 'Light Brightness', - read_only: false, - has_feedback: false, - min: 0, - max: 255, - zigbeeField: 'brightness', - }, - color_temperature: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT, - type: DEVICE_FEATURE_TYPES.LIGHT.TEMPERATURE, - name: 'Light Temperature', - read_only: false, - has_feedback: false, - min: 150, - max: 500, - zigbeeField: 'color_temp', - }, - color: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT, - type: DEVICE_FEATURE_TYPES.LIGHT.COLOR, - name: 'Light Color', - read_only: false, - has_feedback: false, - min: 0, - max: 16777215, - zigbeeField: 'color', - }, - gas: { - category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - name: 'Smoke Sensor', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - zigbeeField: 'gas', - }, - gas_density: { - category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - name: 'Gas Density', - read_only: true, - has_feedback: false, - min: 0, - max: 1000, - zigbeeField: 'gas', - }, - co2: { - category: DEVICE_FEATURE_CATEGORIES.CO2_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - name: 'CO2 Concentration', - read_only: true, - has_feedback: false, - min: 0, - max: 10000, - zigbeeField: 'co2', - }, -}; - -module.exports = { - features, -}; diff --git a/server/services/zigbee2mqtt/utils/loadFeatures.js b/server/services/zigbee2mqtt/utils/loadFeatures.js deleted file mode 100644 index fe72e670a2..0000000000 --- a/server/services/zigbee2mqtt/utils/loadFeatures.js +++ /dev/null @@ -1,33 +0,0 @@ -const { features } = require('./features'); -const { getFeaturesByModel } = require('../model'); - -/** - * @description Retreive feature related to device. - * @param {string} name - Device name. - * @param {string} model - Device model. - * @param {boolean} addBattery - Automatically add battery feature. - * @returns {Object} Device for Gladys. - * @example - * loadFeatures('MyDevice', 'MODEL', true); - */ -function loadFeatures(name, model, addBattery) { - const matchingFeatures = getFeaturesByModel(model); - const loadedFeatures = matchingFeatures.map((f) => { - return Object.assign({}, f); - }); - - if (addBattery) { - loadedFeatures.push(Object.assign({}, features.battery)); - } - - loadedFeatures.forEach((feature) => { - feature.external_id = `zigbee2mqtt:${name}:${feature.category}:${feature.type}:${feature.zigbeeField}`; - feature.selector = feature.external_id; - }); - - return loadedFeatures; -} - -module.exports = { - loadFeatures, -}; diff --git a/server/test/services/zigbee2mqtt/utils/loadFeatures.test.js b/server/test/services/zigbee2mqtt/utils/loadFeatures.test.js deleted file mode 100644 index de2d16d7ed..0000000000 --- a/server/test/services/zigbee2mqtt/utils/loadFeatures.test.js +++ /dev/null @@ -1,59 +0,0 @@ -const { assert } = require('chai'); -const { loadFeatures } = require('../../../../services/zigbee2mqtt/utils/loadFeatures'); - -const name = 'Device'; -const model = 'WXKG11LM'; - -const expectedFeaturesWithBattery = [ - { - category: 'button', - type: 'click', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - name: 'Button', - external_id: 'zigbee2mqtt:Device:button:click:action', - selector: 'zigbee2mqtt:Device:button:click:action', - zigbeeField: 'action', - }, - { - category: 'battery', - type: 'integer', - unit: 'percent', - read_only: true, - has_feedback: false, - min: 0, - max: 100, - name: 'Battery', - external_id: 'zigbee2mqtt:Device:battery:integer:battery', - selector: 'zigbee2mqtt:Device:battery:integer:battery', - zigbeeField: 'battery', - }, -]; - -const expectedFeatures = [ - { - category: 'button', - type: 'click', - read_only: true, - has_feedback: false, - min: 0, - max: 1, - name: 'Button', - external_id: 'zigbee2mqtt:Device:button:click:action', - selector: 'zigbee2mqtt:Device:button:click:action', - zigbeeField: 'action', - }, -]; - -describe('zigbee2mqttService loadFeatures', () => { - it('should return features with battery', () => { - const features = loadFeatures(name, model, true); - return assert.deepEqual(features, expectedFeaturesWithBattery); - }); - it('should return features without battery', () => { - const features = loadFeatures(name, model, false); - return assert.deepEqual(features, expectedFeatures); - }); -}); From 576dcc17ddf6c0e677e38a92fd8067eb70b31986 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:34:37 +0200 Subject: [PATCH 2/9] Zigbee2mqtt auto-build features --- front/src/config/i18n/en.json | 2 + front/src/config/i18n/fr.json | 2 + .../zigbee2mqtt/exposes/binaryType.js | 91 +++++++ .../zigbee2mqtt/exposes/compositeType.js | 15 ++ .../services/zigbee2mqtt/exposes/enumType.js | 39 +++ server/services/zigbee2mqtt/exposes/index.js | 9 + .../zigbee2mqtt/exposes/numericType.js | 238 +++++++++++++++++ .../zigbee2mqtt/lib/handleMqttMessage.js | 4 +- .../zigbee2mqtt/utils/convertDevice.js | 36 +-- .../utils/features/buildFeature.js | 88 +++++++ .../utils/features/completeFeature.js | 24 ++ .../utils/features/mapDefinition.js | 20 ++ .../zigbee2mqtt/utils/features/mapExpose.js | 31 +++ .../zigbee2mqtt/utils/features/mapUnit.js | 41 +++ .../zigbee2mqtt/lib/handleMqttMessage.test.js | 101 +------ .../lib/payloads/event_device_result.json | 106 ++++++++ .../lib/payloads/mqtt_devices_get.json | 246 ++++++++++++++++++ .../zigbee2mqtt/utils/convertDevice.test.js | 96 +++---- .../utils/feature/buildFeature.test.js | 227 ++++++++++++++++ .../utils/feature/completeFeature.test.js | 16 ++ .../zigbee2mqtt/utils/feature/mapUnit.test.js | 31 +++ server/utils/constants.js | 1 + 22 files changed, 1297 insertions(+), 167 deletions(-) create mode 100644 server/services/zigbee2mqtt/exposes/binaryType.js create mode 100644 server/services/zigbee2mqtt/exposes/compositeType.js create mode 100644 server/services/zigbee2mqtt/exposes/enumType.js create mode 100644 server/services/zigbee2mqtt/exposes/index.js create mode 100644 server/services/zigbee2mqtt/exposes/numericType.js create mode 100644 server/services/zigbee2mqtt/utils/features/buildFeature.js create mode 100644 server/services/zigbee2mqtt/utils/features/completeFeature.js create mode 100644 server/services/zigbee2mqtt/utils/features/mapDefinition.js create mode 100644 server/services/zigbee2mqtt/utils/features/mapExpose.js create mode 100644 server/services/zigbee2mqtt/utils/features/mapUnit.js create mode 100644 server/test/services/zigbee2mqtt/lib/payloads/event_device_result.json create mode 100644 server/test/services/zigbee2mqtt/lib/payloads/mqtt_devices_get.json create mode 100644 server/test/services/zigbee2mqtt/utils/feature/buildFeature.test.js create mode 100644 server/test/services/zigbee2mqtt/utils/feature/completeFeature.test.js create mode 100644 server/test/services/zigbee2mqtt/utils/feature/mapUnit.test.js diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 80dec54b5e..acf5805f53 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -1525,6 +1525,7 @@ "kilowatt": "Kilowatt (kW)", "kilowatt-hour": "Kilowatt hour (kWh)", "ampere": "Ampere (A)", + "millivolt": "Millivolt (mV)", "volt": "Volt (V)", "ppm": "Parts per million (ppm)", "cm": "Centimeter (cm)", @@ -1541,6 +1542,7 @@ "kilowatt": "kW", "kilowatt-hour": "kWh", "ampere": "A", + "millivolt": "mV", "volt": "V", "ppm": "ppm", "cm": "cm", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 6d9c1dec74..6bc240ca73 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -1525,6 +1525,7 @@ "kilowatt": "Kilowatt", "kilowatt-hour": "Kilowatt heure (kWh)", "ampere": "Ampère (A)", + "millivolt": "Millivolt (mV)", "volt": "Volt (V)", "ppm": "Partie par million (ppm)", "cm": "Centimètre (cm)", @@ -1541,6 +1542,7 @@ "kilowatt": "kW", "kilowatt-hour": "kWh", "ampere": "A", + "millivolt": "mV", "volt": "V", "ppm": "ppm", "cm": "cm", diff --git a/server/services/zigbee2mqtt/exposes/binaryType.js b/server/services/zigbee2mqtt/exposes/binaryType.js new file mode 100644 index 0000000000..949c97db1a --- /dev/null +++ b/server/services/zigbee2mqtt/exposes/binaryType.js @@ -0,0 +1,91 @@ +const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../../../utils/constants'); + +module.exports = { + type: 'binary', + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.BINARY, + min: 0, + max: 1, + }, + names: { + /* + auto_lock: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + away_mode: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.FAN, + type: DEVICE_FEATURE_TYPES.FAN.BINARY, + }, + }, + carbon_monoxide: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.MONOXIDE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + }, + }, + */ + contact: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.OPENING_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + eco_mode: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + occupancy: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.MOTION_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + interlock: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + presence: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.MOTION_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + smoke: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + state: { + types: { + light: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT, + type: DEVICE_FEATURE_TYPES.LIGHT.BINARY, + }, + lock: { + category: DEVICE_FEATURE_CATEGORIES.ACCESS_CONTROL, + type: DEVICE_FEATURE_TYPES.ACCESS_CONTROL.MODE, + }, + switch: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.BINARY, + }, + }, + }, + water_leak: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LEAK_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + }, +}; diff --git a/server/services/zigbee2mqtt/exposes/compositeType.js b/server/services/zigbee2mqtt/exposes/compositeType.js new file mode 100644 index 0000000000..2db3ea9adc --- /dev/null +++ b/server/services/zigbee2mqtt/exposes/compositeType.js @@ -0,0 +1,15 @@ +const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../../../utils/constants'); + +module.exports = { + type: 'composite', + names: { + color_xy: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT, + type: DEVICE_FEATURE_TYPES.LIGHT.COLOR, + min: 0, + max: 16777215, + }, + }, + }, +}; diff --git a/server/services/zigbee2mqtt/exposes/enumType.js b/server/services/zigbee2mqtt/exposes/enumType.js new file mode 100644 index 0000000000..1ddf759394 --- /dev/null +++ b/server/services/zigbee2mqtt/exposes/enumType.js @@ -0,0 +1,39 @@ +const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } = require('../../../utils/constants'); + +module.exports = { + type: 'enum', + feature: { + min: 0, + max: 1, + }, + names: { + action: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.BUTTON, + type: DEVICE_FEATURE_TYPES.BUTTON.CLICK, + }, + }, + /* + fan_mode: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.FAN, + type: DEVICE_FEATURE_TYPES.FAN.SPEED, + }, + }, + state: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.CURTAIN, + type: DEVICE_FEATURE_TYPES.CURTAIN.STATE, + min: 0, + max: 2, + }, + } + system_mode: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.FAN, + type: DEVICE_FEATURE_TYPES.FAN.MODE, + }, + }, + */ + }, +}; diff --git a/server/services/zigbee2mqtt/exposes/index.js b/server/services/zigbee2mqtt/exposes/index.js new file mode 100644 index 0000000000..6e5e303293 --- /dev/null +++ b/server/services/zigbee2mqtt/exposes/index.js @@ -0,0 +1,9 @@ +const binaryType = require('./binaryType'); +const numericType = require('./numericType'); +const enumType = require('./enumType'); + +module.exports = { + [binaryType.type]: binaryType, + [numericType.type]: numericType, + [enumType.type]: enumType, +}; diff --git a/server/services/zigbee2mqtt/exposes/numericType.js b/server/services/zigbee2mqtt/exposes/numericType.js new file mode 100644 index 0000000000..7d0b0e07dc --- /dev/null +++ b/server/services/zigbee2mqtt/exposes/numericType.js @@ -0,0 +1,238 @@ +const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES, DEVICE_FEATURE_UNITS } = require('../../../utils/constants'); + +module.exports = { + type: 'numeric', + feature: { + min: 0, + max: 10000, + }, + names: { + battery: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.BATTERY, + type: DEVICE_FEATURE_TYPES.SENSOR.INTEGER, + unit: DEVICE_FEATURE_UNITS.PERCENT, + min: 0, + max: 100, + }, + }, + brightness: { + types: { + light: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT, + type: DEVICE_FEATURE_TYPES.LIGHT.BRIGHTNESS, + min: 0, + max: 255, + }, + }, + }, + co2: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.CO2_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.PPM, + }, + }, + color_temp: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT, + type: DEVICE_FEATURE_TYPES.LIGHT.TEMPERATURE, + min: 150, + max: 500, + }, + }, + /* + confort_temperature: { + feature: { + }, + }, + */ + current: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.CURRENT, + unit: DEVICE_FEATURE_UNITS.AMPERE, + min: 0, + max: 1000, + }, + }, + current_phase_b: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.CURRENT, + unit: DEVICE_FEATURE_UNITS.AMPERE, + min: 0, + max: 1000, + }, + }, + current_phase_c: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.CURRENT, + unit: DEVICE_FEATURE_UNITS.AMPERE, + min: 0, + max: 1000, + }, + }, + cpu_temperature: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.CELSIUS, + min: -100, + max: 150, + }, + }, + device_temperature: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.CELSIUS, + min: -100, + max: 150, + }, + }, + eco2: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.CO2_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.PPM, + }, + }, + energy: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.ENERGY, + unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, + }, + }, + formaldehyd: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + }, + }, + gas: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + }, + }, + /* + hue: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT, + type: DEVICE_FEATURE_TYPES.LIGHT.HUE, + min: 150?, + max: 500?, + }, + }, + */ + humidity: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.HUMIDITY_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.PERCENT, + min: 0, + max: 100, + }, + }, + illuminance: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + min: 0, + max: 10000, + }, + }, + illuminance_lux: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.LUX, + min: 0, + max: 10000, + }, + }, + local_temperature: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.CELSIUS, + min: -100, + max: 150, + }, + }, + /* + position: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.CURTAIN, + type: DEVICE_FEATURE_TYPES.CURTAIN.POSITION, + unit: DEVICE_FEATURE_UNITS.PERCENT, + min: 0, + max: 100, + }, + }, + */ + power: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.POWER, + unit: DEVICE_FEATURE_UNITS.WATT, + }, + }, + pressure: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.PRESSURE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.HECTO_PASCAL, + }, + }, + /* + saturation: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.LIGHT, + type: DEVICE_FEATURE_TYPES.LIGHT.SATURATION, + min: 150?, + max: 500?, + }, + }, + */ + temperature: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, + unit: DEVICE_FEATURE_UNITS.CELSIUS, + min: -100, + max: 150, + }, + }, + vibration: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.VIBRATION_SENSOR, + type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, + }, + }, + voltage: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.VOLTAGE, + unit: DEVICE_FEATURE_UNITS.VOLT, + }, + }, + voltage_phase_b: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.VOLTAGE, + unit: DEVICE_FEATURE_UNITS.VOLT, + }, + }, + voltage_phase_c: { + feature: { + category: DEVICE_FEATURE_CATEGORIES.SWITCH, + type: DEVICE_FEATURE_TYPES.SWITCH.VOLTAGE, + unit: DEVICE_FEATURE_UNITS.VOLT, + }, + }, + }, +}; diff --git a/server/services/zigbee2mqtt/lib/handleMqttMessage.js b/server/services/zigbee2mqtt/lib/handleMqttMessage.js index af1231b1c5..0e3a3a51a1 100644 --- a/server/services/zigbee2mqtt/lib/handleMqttMessage.js +++ b/server/services/zigbee2mqtt/lib/handleMqttMessage.js @@ -21,14 +21,12 @@ function handleMqttMessage(topic, message) { }); switch (topic) { - case 'zigbee2mqtt/bridge/config/devices': { + case 'zigbee2mqtt/bridge/devices': { logger.log('Getting config devices from Zigbee2mqtt'); const devices = JSON.parse(message); const convertedDevices = devices // Remove Coordinator .filter((d) => d.type !== TYPE_COORDINATOR) - // Remove Empty models - .filter((d) => d.model && d.model !== '') // Remove existing devices .filter((d) => { const existingDevice = this.gladys.stateManager.get('deviceByExternalId', `zigbee2mqtt:${d.friendly_name}`); diff --git a/server/services/zigbee2mqtt/utils/convertDevice.js b/server/services/zigbee2mqtt/utils/convertDevice.js index 393d99e6c7..7f3afe0836 100644 --- a/server/services/zigbee2mqtt/utils/convertDevice.js +++ b/server/services/zigbee2mqtt/utils/convertDevice.js @@ -1,6 +1,6 @@ -const { loadFeatures } = require('./loadFeatures'); const logger = require('../../../utils/logger'); const { DEVICE_FEATURE_CATEGORIES } = require('../../../../server/utils/constants'); +const { mapDefinition } = require('./features/mapDefinition'); /** * @description Converts an MQTT device to a Gladys device. @@ -8,37 +8,27 @@ const { DEVICE_FEATURE_CATEGORIES } = require('../../../../server/utils/constant * @param {string} serviceId - Service ID. * @returns {Object} Device for Gladys. * @example - * convertDevice({ friendly_name: 'name', model: 'featureMapper' }, '6a37dd9d-48c7-4d09-a7bb-33f257edb78d'); + * convertDevice({ friendly_name: 'name', definition: {} }, '6a37dd9d-48c7-4d09-a7bb-33f257edb78d'); */ function convertDevice(device, serviceId) { - const features = loadFeatures(device.friendly_name, device.model, device.powerSource === 'Battery'); + const { friendly_name: name, definition = {} } = device; + const { model } = definition; + const features = mapDefinition(name, definition); - // Not managed device - if (features.length === 0 || (features.length === 1 && features[0].category === DEVICE_FEATURE_CATEGORIES.BATTERY)) { - const gladysDevice = { - name: device.friendly_name, - external_id: `zigbee2mqtt:${device.friendly_name}`, - model: device.model, - features, - should_poll: false, - service_id: serviceId, - supported: false, - }; - logger.debug(`Device ${device.friendly_name} / model ${device.model} NOT managed by Gladys`); - logger.debug(gladysDevice); - return gladysDevice; - } + // Device is not managed if no feature found, or only battery feature is available. + const supported = features.findIndex((f) => f.category !== DEVICE_FEATURE_CATEGORIES.BATTERY) >= 0; const gladysDevice = { - name: device.friendly_name, - external_id: `zigbee2mqtt:${device.friendly_name}`, - model: device.model, + name, + model, + external_id: `zigbee2mqtt:${name}`, features, should_poll: false, service_id: serviceId, - supported: true, + supported, }; - logger.debug(`Device ${device.friendly_name} / model ${device.model} managed by Gladys`); + + logger.debug(`Device ${name} / model ${model} ${supported ? '' : 'NOT'} managed by Gladys`); logger.debug(gladysDevice); return gladysDevice; } diff --git a/server/services/zigbee2mqtt/utils/features/buildFeature.js b/server/services/zigbee2mqtt/utils/features/buildFeature.js new file mode 100644 index 0000000000..1e3e5ec839 --- /dev/null +++ b/server/services/zigbee2mqtt/utils/features/buildFeature.js @@ -0,0 +1,88 @@ +const exposesMap = require('../../exposes'); +const { mapUnit } = require('./mapUnit'); +const { completeFeature } = require('./completeFeature'); + +/** + * @description Load feature from parent type. + * @param {Object} types - Zigbee "expose" parent type features. + * @param {string} parentType - Requested parent type. + * @returns {Object} The related Gladys feature, or undefined. + * + * @example buildByParentType({ switch: {}, light: {}}, 'light'); + */ +function buildByParentType(types = {}, parentType) { + return types[parentType]; +} + +/** + * @description Load feature from property name, completed by parent type. + * + * @param {Object} names - Zigbee "expose" proerty name features. + * @param {string} name - Zigbee "expose" property name. + * @param {string} parentType - Requested parent type. + * @returns {Object} The related Gladys feature, or undefined. + * + * @example buildByName({ state: {}}, 'state', 'light'); + */ +function buildByName(names = {}, name, parentType) { + const { types = {}, feature } = names[name] || {}; + const byType = buildByParentType(types, parentType); + + if (!byType && !feature) { + return undefined; + } + + return { ...(feature || {}), ...(byType || {}) }; +} + +/** + * @description Build a Gladys feature according to Zigbee "expose" values. + * @param {string} deviceName - Device friendly name. + * @param {Object} expose - Zigbee "expose" values. + * @param {string} parentType - Requested parent type. + * @returns {Object} The related Gladys feature, or undefined. + * + * @example buildFeature('MyDevice', {}, 'light'); + */ +function buildFeature(deviceName, expose = {}, parentType) { + const { type, name, property, access, value_min: minValue, value_max: maxValue, unit: deviceUnit, values } = expose; + const { names = {}, feature } = exposesMap[type] || {}; + const byName = buildByName(names, name, parentType); + + if (!byName) { + return undefined; + } + + // Read only ? + // eslint-disable-next-line no-bitwise + const readOnly = (access & 2) === 0; + + // Has feedback ? + // eslint-disable-next-line no-bitwise + const hasFeedback = !readOnly && (access & 1) === 1; + + const createdFeature = { read_only: readOnly, has_feedback: hasFeedback, ...(feature || {}), ...(byName || {}) }; + + // Min value + const min = minValue !== undefined ? minValue : createdFeature.min; + + // Max value + let { max } = createdFeature; + if (maxValue !== undefined) { + max = maxValue; + } else if (values !== undefined) { + max = values.length; + } + + // Unit + const unit = mapUnit(deviceUnit, createdFeature.unit); + + // Add missing properties + return completeFeature(deviceName, { ...createdFeature, min, max, unit }, property); +} + +module.exports = { + buildByParentType, + buildByName, + buildFeature, +}; diff --git a/server/services/zigbee2mqtt/utils/features/completeFeature.js b/server/services/zigbee2mqtt/utils/features/completeFeature.js new file mode 100644 index 0000000000..6929716921 --- /dev/null +++ b/server/services/zigbee2mqtt/utils/features/completeFeature.js @@ -0,0 +1,24 @@ +const { addSelector } = require('../../../../utils/addSelector'); + +/** + * @description Complete feature with externalId and selector generated values. + * + * @param {string} deviceName - Zigbee friendly device name. + * @param {Object} feature - Related Gladys feature. + * @param {string} property - Related Zigbee property. + * @returns {Object} Completed Galdys feature. + * + * @example completeFeature('MyDevice', { category: 'light', type: 'binary' }, 'state'); + */ +function completeFeature(deviceName, feature, property) { + const { category, type } = feature; + const externalId = `zigbee2mqtt:${deviceName}:${category}:${type}:${property}`; + const name = `${property.charAt(0).toUpperCase() + property.slice(1).replace(/_/g, ' ')}`; + const completedFeature = { name, ...feature, external_id: externalId, selector: externalId }; + addSelector(completedFeature); + return completedFeature; +} + +module.exports = { + completeFeature, +}; diff --git a/server/services/zigbee2mqtt/utils/features/mapDefinition.js b/server/services/zigbee2mqtt/utils/features/mapDefinition.js new file mode 100644 index 0000000000..301b4a603a --- /dev/null +++ b/server/services/zigbee2mqtt/utils/features/mapDefinition.js @@ -0,0 +1,20 @@ +const { mapExpose } = require('./mapExpose'); + +/** + * @description Build Gladys features according to Zigbee device. + * @param {string} deviceName - Device name. + * @param {Object} definition - Zigbee device definition. + * @returns {Array} The related Gladys features. + * + * @example mapDefinition('MyDevice', {}); + */ +function mapDefinition(deviceName, definition = {}) { + const { exposes = [] } = definition; + + // Build feature according to device + return exposes.flatMap((e) => mapExpose(deviceName, e)); +} + +module.exports = { + mapDefinition, +}; diff --git a/server/services/zigbee2mqtt/utils/features/mapExpose.js b/server/services/zigbee2mqtt/utils/features/mapExpose.js new file mode 100644 index 0000000000..e4ed686d64 --- /dev/null +++ b/server/services/zigbee2mqtt/utils/features/mapExpose.js @@ -0,0 +1,31 @@ +const { buildFeature } = require('./buildFeature'); + +/** + * @description Build a Gladys feature according to Zigbee "expose" and "features" values. + * @param {string} deviceName - Device friendly name. + * @param {Object} expose - Zigbee "expose" values. + * @param {string} parentType - Requested parent type. + * @returns {Object} The related Gladys feature, or undefined. + * + * @example mapExpose('MyDevice', {}, 'light'); + */ +function mapExpose(deviceName, expose, parentType = undefined) { + const { type, features = [] } = expose; + + const matchingFeatures = []; + + // Merge default with specific + const feature = buildFeature(deviceName, expose, parentType); + if (feature) { + matchingFeatures.push(feature); + } + + // Map exposed sub-features recursivly + features.flatMap((f) => mapExpose(deviceName, f, parentType || type)).forEach((f) => matchingFeatures.push(f)); + + return matchingFeatures; +} + +module.exports = { + mapExpose, +}; diff --git a/server/services/zigbee2mqtt/utils/features/mapUnit.js b/server/services/zigbee2mqtt/utils/features/mapUnit.js new file mode 100644 index 0000000000..bf6c258445 --- /dev/null +++ b/server/services/zigbee2mqtt/utils/features/mapUnit.js @@ -0,0 +1,41 @@ +const { DEVICE_FEATURE_UNITS } = require('../../../../utils/constants'); + +/** + * @description Transform Zigbee unit to Gladys unit. + * + * @param {string} deviceUnit - Zigbee unit. + * @param {string} featureUnit - Default unit. + * @returns {string} Tranformed unit. + * + * @example mapUnit('°C', 'celsius'); + */ +function mapUnit(deviceUnit, featureUnit) { + switch (deviceUnit) { + case '%': + return DEVICE_FEATURE_UNITS.PERCENT; + case 'hPa': + return DEVICE_FEATURE_UNITS.HECTO_PASCAL; + case 'ppm': + return DEVICE_FEATURE_UNITS.PPM; + case 'A': + return DEVICE_FEATURE_UNITS.AMPERE; + case 'V': + return DEVICE_FEATURE_UNITS.VOLT; + case 'mV': + return DEVICE_FEATURE_UNITS.MILLI_VOLT; + case 'W': + return DEVICE_FEATURE_UNITS.WATT; + case 'kWh': + return DEVICE_FEATURE_UNITS.KILOWATT_HOUR; + case '°C': + return DEVICE_FEATURE_UNITS.CELSIUS; + case '°F': + return DEVICE_FEATURE_UNITS.FAHRENHEIT; + default: + return featureUnit; + } +} + +module.exports = { + mapUnit, +}; diff --git a/server/test/services/zigbee2mqtt/lib/handleMqttMessage.test.js b/server/test/services/zigbee2mqtt/lib/handleMqttMessage.test.js index 4cde5b8c2d..795f90b011 100644 --- a/server/test/services/zigbee2mqtt/lib/handleMqttMessage.test.js +++ b/server/test/services/zigbee2mqtt/lib/handleMqttMessage.test.js @@ -3,6 +3,9 @@ const sinon = require('sinon'); const { assert, fake } = sinon; const proxyquire = require('proxyquire').noCallThru(); +const zigbeeDevices = require('./payloads/mqtt_devices_get.json'); +const expectedDevicesPayload = require('./payloads/event_device_result.json'); + const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../../../utils/constants'); const Zigbee2mqttManager = proxyquire('../../../../services/zigbee2mqtt/lib', {}); @@ -21,103 +24,7 @@ const gladys = { }, }; -const zigbeeDevices = `[ - { - "type":"Coordinator" - }, - { - "friendly_name":"0x00158d0005828ece", - "model":"WSDCGQ11LM", - "powerSource":"Battery", - "type":"EndDevice" - }, - { - "friendly_name":"0x00158d0005828eca", - "model":"fakeModel", - "type":"EndDevice" - }, - { - "friendly_name":"0x00158d0005828ece", - "model":"WSDCGQ11LM", - "powerSource":"Battery", - "type":"EndDevice" - } - ]`; - const serviceId = 'f87b7af2-ca8e-44fc-b754-444354b42fee'; -const expectedDevicesPayload = [ - { - external_id: 'zigbee2mqtt:0x00158d0005828eca', - features: [], - model: 'fakeModel', - name: '0x00158d0005828eca', - service_id: 'f87b7af2-ca8e-44fc-b754-444354b42fee', - should_poll: false, - supported: false, - }, - { - external_id: 'zigbee2mqtt:0x00158d0005828ece', - features: [ - { - category: 'temperature-sensor', - external_id: 'zigbee2mqtt:0x00158d0005828ece:temperature-sensor:decimal:temperature', - has_feedback: false, - max: 125, - min: -50, - read_only: true, - name: 'Temperature', - selector: 'zigbee2mqtt:0x00158d0005828ece:temperature-sensor:decimal:temperature', - type: 'decimal', - unit: 'celsius', - zigbeeField: 'temperature', - }, - { - category: 'humidity-sensor', - external_id: 'zigbee2mqtt:0x00158d0005828ece:humidity-sensor:decimal:humidity', - has_feedback: false, - max: 100, - min: 0, - read_only: true, - name: 'Humidity', - selector: 'zigbee2mqtt:0x00158d0005828ece:humidity-sensor:decimal:humidity', - type: 'decimal', - unit: 'percent', - zigbeeField: 'humidity', - }, - { - category: 'pressure-sensor', - external_id: 'zigbee2mqtt:0x00158d0005828ece:pressure-sensor:decimal:pressure', - has_feedback: false, - max: 10000, - min: 0, - name: 'Pressure Sensor', - read_only: true, - selector: 'zigbee2mqtt:0x00158d0005828ece:pressure-sensor:decimal:pressure', - type: 'decimal', - unit: 'hPa', - zigbeeField: 'pressure', - }, - { - category: 'battery', - external_id: 'zigbee2mqtt:0x00158d0005828ece:battery:integer:battery', - has_feedback: false, - max: 100, - min: 0, - read_only: true, - name: 'Battery', - selector: 'zigbee2mqtt:0x00158d0005828ece:battery:integer:battery', - type: 'integer', - unit: 'percent', - zigbeeField: 'battery', - }, - ], - model: 'WSDCGQ11LM', - name: '0x00158d0005828ece', - service_id: 'f87b7af2-ca8e-44fc-b754-444354b42fee', - should_poll: false, - supported: true, - }, -]; describe('zigbee2mqtt handleMqttMessage', () => { // PREPARE @@ -141,7 +48,7 @@ describe('zigbee2mqtt handleMqttMessage', () => { .returns(false); zigbee2mqttManager.gladys.stateManager.get = stateManagerGetStub; // EXECUTE - await zigbee2mqttManager.handleMqttMessage('zigbee2mqtt/bridge/config/devices', zigbeeDevices); + await zigbee2mqttManager.handleMqttMessage('zigbee2mqtt/bridge/devices', JSON.stringify(zigbeeDevices)); // ASSERT assert.calledWith(gladys.event.emit, EVENTS.WEBSOCKET.SEND_ALL, { type: WEBSOCKET_MESSAGE_TYPES.ZIGBEE2MQTT.STATUS_CHANGE, diff --git a/server/test/services/zigbee2mqtt/lib/payloads/event_device_result.json b/server/test/services/zigbee2mqtt/lib/payloads/event_device_result.json new file mode 100644 index 0000000000..ac77dc0037 --- /dev/null +++ b/server/test/services/zigbee2mqtt/lib/payloads/event_device_result.json @@ -0,0 +1,106 @@ +[ + { + "name": "0x00158d0004019127", + "model": "WXKG11LM", + "external_id": "zigbee2mqtt:0x00158d0004019127", + "features": [ + { + "name": "Battery", + "read_only": true, + "has_feedback": false, + "min": 0, + "max": 100, + "category": "battery", + "type": "integer", + "unit": "percent", + "external_id": "zigbee2mqtt:0x00158d0004019127:battery:integer:battery", + "selector": "zigbee2mqtt-0x00158d0004019127-battery-integer-battery" + }, + { + "name": "Voltage", + "read_only": true, + "has_feedback": false, + "min": 0, + "max": 10000, + "category": "switch", + "type": "voltage", + "unit": "millivolt", + "external_id": "zigbee2mqtt:0x00158d0004019127:switch:voltage:voltage", + "selector": "zigbee2mqtt-0x00158d0004019127-switch-voltage-voltage" + } + ], + "should_poll": false, + "service_id": "f87b7af2-ca8e-44fc-b754-444354b42fee", + "supported": true + }, + { + "name": "0x00158d00045b2740", + "model": "WSDCGQ11LM", + "external_id": "zigbee2mqtt:0x00158d00045b2740", + "features": [ + { + "name": "Battery", + "read_only": true, + "has_feedback": false, + "min": 0, + "max": 100, + "category": "battery", + "type": "integer", + "unit": "percent", + "external_id": "zigbee2mqtt:0x00158d00045b2740:battery:integer:battery", + "selector": "zigbee2mqtt-0x00158d00045b2740-battery-integer-battery" + }, + { + "name": "Temperature", + "read_only": true, + "has_feedback": false, + "min": -100, + "max": 150, + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "external_id": "zigbee2mqtt:0x00158d00045b2740:temperature-sensor:decimal:temperature", + "selector": "zigbee2mqtt-0x00158d00045b2740-temperature-sensor-decimal-temperature" + }, + { + "name": "Humidity", + "read_only": true, + "has_feedback": false, + "min": 0, + "max": 100, + "category": "humidity-sensor", + "type": "decimal", + "unit": "percent", + "external_id": "zigbee2mqtt:0x00158d00045b2740:humidity-sensor:decimal:humidity", + "selector": "zigbee2mqtt-0x00158d00045b2740-humidity-sensor-decimal-humidity" + }, + { + "name": "Pressure", + "read_only": true, + "has_feedback": false, + "min": 0, + "max": 10000, + "category": "pressure-sensor", + "type": "decimal", + "unit": "hPa", + "external_id": "zigbee2mqtt:0x00158d00045b2740:pressure-sensor:decimal:pressure", + "selector": "zigbee2mqtt-0x00158d00045b2740-pressure-sensor-decimal-pressure" + }, + { + "name": "Voltage", + "read_only": true, + "has_feedback": false, + "min": 0, + "max": 10000, + "category": "switch", + "type": "voltage", + "unit": "millivolt", + "external_id": "zigbee2mqtt:0x00158d00045b2740:switch:voltage:voltage", + "selector": "zigbee2mqtt-0x00158d00045b2740-switch-voltage-voltage" + } + ], + "should_poll": false, + "service_id": "f87b7af2-ca8e-44fc-b754-444354b42fee", + "supported": true + } +] diff --git a/server/test/services/zigbee2mqtt/lib/payloads/mqtt_devices_get.json b/server/test/services/zigbee2mqtt/lib/payloads/mqtt_devices_get.json new file mode 100644 index 0000000000..574aa5d2ec --- /dev/null +++ b/server/test/services/zigbee2mqtt/lib/payloads/mqtt_devices_get.json @@ -0,0 +1,246 @@ +[ + { + "definition": null, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": ["genBasic", "genTime", "genOta"], + "output": ["genPowerCfg", "genPollCtrl", "ssIasZone"] + }, + "configured_reportings": [] + }, + "242": { + "bindings": [], + "clusters": { + "input": [], + "output": ["greenPower"] + }, + "configured_reportings": [] + } + }, + "friendly_name": "Coordinator", + "ieee_address": "0x00212effff06ddd8", + "interview_completed": true, + "interviewing": false, + "network_address": 0, + "supported": false, + "type": "Coordinator" + }, + { + "definition": { + "description": "MiJia wireless switch", + "exposes": [ + { + "access": 1, + "description": "Remaining battery in %", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Triggered action (e.g. a button click)", + "name": "action", + "property": "action", + "type": "enum", + "values": ["single", "double", "triple", "quadruple", "hold", "release", "many"] + }, + { + "access": 1, + "description": "Voltage of the battery in millivolts", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "description": "Link quality (signal strength)", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "WXKG01LM", + "supports_ota": false, + "vendor": "Xiaomi" + }, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [], + "output": [] + }, + "configured_reportings": [] + } + }, + "friendly_name": "0x00158d00033e88d5", + "ieee_address": "0x00158d00033e88d5", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.sensor_switch", + "network_address": 44173, + "power_source": "Battery", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20180525", + "definition": { + "description": "Aqara wireless switch", + "exposes": [ + { + "access": 1, + "description": "Remaining battery in %", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Voltage of the battery in millivolts", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "description": "Link quality (signal strength)", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "WXKG11LM", + "supports_ota": false, + "vendor": "Xiaomi" + }, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": ["genBasic", "genMultistateInput", "genIdentify"], + "output": ["genBasic"] + }, + "configured_reportings": [] + } + }, + "friendly_name": "0x00158d0004019127", + "ieee_address": "0x00158d0004019127", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.remote.b1acn01", + "network_address": 47250, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + }, + { + "date_code": "20161129", + "definition": { + "description": "Aqara temperature, humidity and pressure sensor", + "exposes": [ + { + "access": 1, + "description": "Remaining battery in %", + "name": "battery", + "property": "battery", + "type": "numeric", + "unit": "%", + "value_max": 100, + "value_min": 0 + }, + { + "access": 1, + "description": "Measured temperature value", + "name": "temperature", + "property": "temperature", + "type": "numeric", + "unit": "°C" + }, + { + "access": 1, + "description": "Measured relative humidity", + "name": "humidity", + "property": "humidity", + "type": "numeric", + "unit": "%" + }, + { + "access": 1, + "description": "The measured atmospheric pressure", + "name": "pressure", + "property": "pressure", + "type": "numeric", + "unit": "hPa" + }, + { + "access": 1, + "description": "Voltage of the battery in millivolts", + "name": "voltage", + "property": "voltage", + "type": "numeric", + "unit": "mV" + }, + { + "access": 1, + "description": "Link quality (signal strength)", + "name": "linkquality", + "property": "linkquality", + "type": "numeric", + "unit": "lqi", + "value_max": 255, + "value_min": 0 + } + ], + "model": "WSDCGQ11LM", + "supports_ota": false, + "vendor": "Xiaomi" + }, + "endpoints": { + "1": { + "bindings": [], + "clusters": { + "input": [ + "genBasic", + "genIdentify", + "msTemperatureMeasurement", + "msPressureMeasurement", + "msRelativeHumidity" + ], + "output": ["genBasic", "genGroups"] + }, + "configured_reportings": [] + } + }, + "friendly_name": "0x00158d00045b2740", + "ieee_address": "0x00158d00045b2740", + "interview_completed": true, + "interviewing": false, + "manufacturer": "LUMI", + "model_id": "lumi.weather", + "network_address": 23007, + "power_source": "Battery", + "software_build_id": "3000-0001", + "supported": true, + "type": "EndDevice" + } +] diff --git a/server/test/services/zigbee2mqtt/utils/convertDevice.test.js b/server/test/services/zigbee2mqtt/utils/convertDevice.test.js index d4f6f57196..54c299ed07 100644 --- a/server/test/services/zigbee2mqtt/utils/convertDevice.test.js +++ b/server/test/services/zigbee2mqtt/utils/convertDevice.test.js @@ -2,57 +2,65 @@ const { assert } = require('chai'); const { convertDevice } = require('../../../../services/zigbee2mqtt/utils/convertDevice'); const serviceId = '6a37dd9d-48c7-4d09-a7bb-33f257edb78d'; - -const notManagedDevice = { - friendly_name: 'Not supported device', - model: 'not_supported_device', -}; - -const managedDevice = { - friendly_name: 'Xiaomi Aqara Sensor', - model: 'WXKG11LM', -}; - -const notManagedGladysDevice = { - name: notManagedDevice.friendly_name, - external_id: `zigbee2mqtt:${notManagedDevice.friendly_name}`, - model: notManagedDevice.model, - features: [], - should_poll: false, - service_id: serviceId, - supported: false, -}; - -const managedGladysDevice = { - name: managedDevice.friendly_name, - external_id: `zigbee2mqtt:${managedDevice.friendly_name}`, - model: managedDevice.model, - features: [ - { - category: 'button', - external_id: `zigbee2mqtt:${managedDevice.friendly_name}:button:click:action`, - has_feedback: false, - max: 1, - min: 0, - read_only: true, - name: 'Button', - selector: `zigbee2mqtt:${managedDevice.friendly_name}:button:click:action`, - type: 'click', - zigbeeField: 'action', - }, - ], - should_poll: false, - service_id: serviceId, - supported: true, -}; - describe('zigbee2mqtt convertDevice', () => { it('should return not managed device', () => { + const notManagedDevice = { + friendly_name: 'Not supported device', + }; + const result = convertDevice(notManagedDevice, serviceId); + + const notManagedGladysDevice = { + name: notManagedDevice.friendly_name, + external_id: `zigbee2mqtt:${notManagedDevice.friendly_name}`, + model: undefined, + features: [], + should_poll: false, + service_id: serviceId, + supported: false, + }; return assert.deepEqual(result, notManagedGladysDevice); }); + it('should return managed device', () => { + const managedDevice = { + friendly_name: 'Xiaomi Aqara Sensor', + definition: { + exposes: [ + { + type: 'enum', + name: 'action', + property: 'action', + }, + ], + model: 'WXKG11LM', + }, + }; + const result = convertDevice(managedDevice, serviceId); + + const managedGladysDevice = { + name: managedDevice.friendly_name, + external_id: `zigbee2mqtt:${managedDevice.friendly_name}`, + model: 'WXKG11LM', + features: [ + { + category: 'button', + external_id: `zigbee2mqtt:${managedDevice.friendly_name}:button:click:action`, + has_feedback: false, + max: 1, + min: 0, + read_only: true, + name: 'Action', + selector: `zigbee2mqtt-xiaomi-aqara-sensor-button-click-action`, + type: 'click', + unit: undefined, + }, + ], + should_poll: false, + service_id: serviceId, + supported: true, + }; return assert.deepEqual(result, managedGladysDevice); }); }); diff --git a/server/test/services/zigbee2mqtt/utils/feature/buildFeature.test.js b/server/test/services/zigbee2mqtt/utils/feature/buildFeature.test.js new file mode 100644 index 0000000000..dda5974d88 --- /dev/null +++ b/server/test/services/zigbee2mqtt/utils/feature/buildFeature.test.js @@ -0,0 +1,227 @@ +const { expect } = require('chai'); + +const { + buildByParentType, + buildByName, + buildFeature, +} = require('../../../../../services/zigbee2mqtt/utils/features/buildFeature'); + +describe('zigbee2mqtt buildByParentType', () => { + it(`no type map`, () => { + const result = buildByParentType(undefined, undefined); + expect(result).eq(undefined); + }); + + it(`no selected type`, () => { + const types = { + binary: 'binary', + }; + const result = buildByParentType(types, undefined); + expect(result).eq(undefined); + }); + + it(`get selected type`, () => { + const types = { + binary: 'binary', + }; + const result = buildByParentType(types, 'binary'); + expect(result).eq('binary'); + }); +}); + +describe('zigbee2mqtt buildByName', () => { + it(`all empty`, () => { + const result = buildByName(undefined, 'binary', 'light'); + expect(result).eq(undefined); + }); + + it(`only by name`, () => { + const names = { + binary: { + feature: { + type: 'binary', + }, + }, + }; + const result = buildByName(names, 'binary', 'light'); + + const expectedResult = { type: 'binary' }; + expect(result).deep.eq(expectedResult); + }); + + it(`name and type`, () => { + const names = { + binary: { + feature: { + type: 'binary', + }, + types: { + light: { + category: 'light', + }, + }, + }, + }; + const result = buildByName(names, 'binary', 'light'); + + const expectedResult = { type: 'binary', category: 'light' }; + expect(result).deep.eq(expectedResult); + }); +}); + +describe('zigbee2mqtt buildFeature', () => { + it(`no data`, () => { + const result = buildFeature('deviceName', undefined, undefined); + expect(result).eq(undefined); + }); + + it(`readOnly = true / hasFeedback = false`, () => { + const expose = { + type: 'binary', + name: 'state', + property: 'property', + access: 1, + }; + const result = buildFeature('deviceName', expose, 'switch'); + + const expectedResult = { + category: 'switch', + type: 'binary', + min: 0, + max: 1, + read_only: true, + has_feedback: false, + name: 'Property', + external_id: 'zigbee2mqtt:deviceName:switch:binary:property', + selector: 'zigbee2mqtt-devicename-switch-binary-property', + unit: undefined, + }; + + expect(result).deep.eq(expectedResult); + }); + + it(`readOnly = false / hasFeedback = false`, () => { + const expose = { + type: 'binary', + name: 'state', + property: 'property', + access: 2, + }; + const result = buildFeature('deviceName', expose, 'switch'); + + const expectedResult = { + category: 'switch', + type: 'binary', + min: 0, + max: 1, + read_only: false, + has_feedback: false, + name: 'Property', + external_id: 'zigbee2mqtt:deviceName:switch:binary:property', + selector: 'zigbee2mqtt-devicename-switch-binary-property', + unit: undefined, + }; + + expect(result).deep.eq(expectedResult); + }); + + it(`readOnly = false / hasFeedback = true`, () => { + const expose = { + type: 'binary', + name: 'state', + property: 'property', + access: 7, + }; + const result = buildFeature('deviceName', expose, 'switch'); + + const expectedResult = { + category: 'switch', + type: 'binary', + min: 0, + max: 1, + read_only: false, + has_feedback: true, + name: 'Property', + external_id: 'zigbee2mqtt:deviceName:switch:binary:property', + selector: 'zigbee2mqtt-devicename-switch-binary-property', + unit: undefined, + }; + + expect(result).deep.eq(expectedResult); + }); + + it(`override min value`, () => { + const expose = { + type: 'binary', + name: 'state', + property: 'property', + value_min: -1, + }; + const result = buildFeature('deviceName', expose, 'switch'); + + const expectedResult = { + category: 'switch', + type: 'binary', + min: -1, + max: 1, + read_only: true, + has_feedback: false, + name: 'Property', + external_id: 'zigbee2mqtt:deviceName:switch:binary:property', + selector: 'zigbee2mqtt-devicename-switch-binary-property', + unit: undefined, + }; + + expect(result).deep.eq(expectedResult); + }); + + it(`override max value`, () => { + const expose = { + type: 'binary', + name: 'state', + property: 'property', + value_max: 10, + }; + const result = buildFeature('deviceName', expose, 'switch'); + + const expectedResult = { + category: 'switch', + type: 'binary', + min: 0, + max: 10, + read_only: true, + has_feedback: false, + name: 'Property', + external_id: 'zigbee2mqtt:deviceName:switch:binary:property', + selector: 'zigbee2mqtt-devicename-switch-binary-property', + unit: undefined, + }; + + expect(result).deep.eq(expectedResult); + }); + + it(`override max by values`, () => { + const expose = { + type: 'binary', + name: 'state', + property: 'property', + values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], // length 10 + }; + const result = buildFeature('deviceName', expose, 'switch'); + + const expectedResult = { + category: 'switch', + type: 'binary', + min: 0, + max: 10, + read_only: true, + has_feedback: false, + name: 'Property', + external_id: 'zigbee2mqtt:deviceName:switch:binary:property', + selector: 'zigbee2mqtt-devicename-switch-binary-property', + unit: undefined, + }; + + expect(result).deep.eq(expectedResult); + }); +}); diff --git a/server/test/services/zigbee2mqtt/utils/feature/completeFeature.test.js b/server/test/services/zigbee2mqtt/utils/feature/completeFeature.test.js new file mode 100644 index 0000000000..aebbb3bea0 --- /dev/null +++ b/server/test/services/zigbee2mqtt/utils/feature/completeFeature.test.js @@ -0,0 +1,16 @@ +const { expect } = require('chai'); + +const { completeFeature } = require('../../../../../services/zigbee2mqtt/utils/features/completeFeature'); + +describe('zigbee2mqtt completeFeature', () => { + it(`completeFeature`, () => { + const result = completeFeature('device name', {}, 'property'); + + const expected = { + name: 'Property', + external_id: 'zigbee2mqtt:device name:undefined:undefined:property', + selector: 'zigbee2mqtt-device-name-undefined-undefined-property', + }; + expect(result).deep.eq(expected); + }); +}); diff --git a/server/test/services/zigbee2mqtt/utils/feature/mapUnit.test.js b/server/test/services/zigbee2mqtt/utils/feature/mapUnit.test.js new file mode 100644 index 0000000000..e7d2e03d1b --- /dev/null +++ b/server/test/services/zigbee2mqtt/utils/feature/mapUnit.test.js @@ -0,0 +1,31 @@ +const { expect } = require('chai'); + +const { mapUnit } = require('../../../../../services/zigbee2mqtt/utils/features/mapUnit'); +const { DEVICE_FEATURE_UNITS } = require('../../../../../utils/constants'); + +describe('zigbee2mqtt mapUnit', () => { + const defaultFeatureUnit = 'default'; + + const values = [ + { input: null, expected: defaultFeatureUnit }, + { input: '%', expected: DEVICE_FEATURE_UNITS.PERCENT }, + { input: 'hPa', expected: DEVICE_FEATURE_UNITS.HECTO_PASCAL }, + { input: 'ppm', expected: DEVICE_FEATURE_UNITS.PPM }, + { input: 'A', expected: DEVICE_FEATURE_UNITS.AMPERE }, + { input: 'V', expected: DEVICE_FEATURE_UNITS.VOLT }, + { input: 'mV', expected: DEVICE_FEATURE_UNITS.MILLI_VOLT }, + { input: 'W', expected: DEVICE_FEATURE_UNITS.WATT }, + { input: 'kWh', expected: DEVICE_FEATURE_UNITS.KILOWATT_HOUR }, + { input: '°C', expected: DEVICE_FEATURE_UNITS.CELSIUS }, + { input: '°F', expected: DEVICE_FEATURE_UNITS.FAHRENHEIT }, + ]; + + values.forEach((value) => { + const { input, expected } = value; + + it(`map "${input}" to ${expected}`, () => { + const result = mapUnit(input, defaultFeatureUnit); + expect(result).eq(expected); + }); + }); +}); diff --git a/server/utils/constants.js b/server/utils/constants.js index b7057e5834..455c77b6ae 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -392,6 +392,7 @@ const DEVICE_FEATURE_UNITS = { KILOWATT: 'kilowatt', KILOWATT_HOUR: 'kilowatt-hour', AMPERE: 'ampere', + MILLI_VOLT: 'millivolt', VOLT: 'volt', PPM: 'ppm', MM: 'mm', From 080a02624653931db152aad3ed795440f5066995 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Sun, 3 Oct 2021 23:17:19 +0200 Subject: [PATCH 3/9] Handle discovered devices --- front/src/config/i18n/en.json | 1 - front/src/config/i18n/fr.json | 1 - .../zigbee2mqtt/discover-page/DiscoverTab.jsx | 9 ------ .../all/zigbee2mqtt/discover-page/actions.js | 28 +++++-------------- .../all/zigbee2mqtt/discover-page/index.js | 1 + .../zigbee2mqtt/api/zigbee2mqtt.controller.js | 16 +++++------ .../zigbee2mqtt/lib/discoverDevices.js | 15 ---------- .../zigbee2mqtt/lib/getDiscoveredDevices.js | 13 +++++++++ .../zigbee2mqtt/lib/handleMqttMessage.js | 6 ++-- server/services/zigbee2mqtt/lib/index.js | 5 ++-- .../api/zigbee2mqtt.controller.test.js | 10 +++---- .../zigbee2mqtt/lib/discoverDevices.test.js | 27 ------------------ .../lib/getDiscoveredDevices.test.js | 18 ++++++++++++ 13 files changed, 58 insertions(+), 92 deletions(-) delete mode 100644 server/services/zigbee2mqtt/lib/discoverDevices.js create mode 100644 server/services/zigbee2mqtt/lib/getDiscoveredDevices.js delete mode 100644 server/test/services/zigbee2mqtt/lib/discoverDevices.test.js create mode 100644 server/test/services/zigbee2mqtt/lib/getDiscoveredDevices.test.js diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index acf5805f53..c3e75fae88 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -505,7 +505,6 @@ "device": "({{nbDevices}}) Devices", "permitJoin": "Permit joining", "noDeviceDiscovered": "No new device discovered, please click on scan button.", - "scanButton": "Scan", "serverNoResponse": "Gladys server is not available.", "serverNoResponseWebSocker": "Gladys server is not available. Pleash refresh the page.", "deviceNotHandled": "Device not handled yet, please contact us to help us connect it in Gladys!", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 6bc240ca73..9107b74e83 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -632,7 +632,6 @@ "device": "({{nbDevices}}) Appareils", "permitJoin": "Autoriser l'association", "noDeviceDiscovered": "Aucun nouvel appareil trouvé. Veuillez autoriser l'association des appareils et les appairer.\nATTENTION : Pour la sécurité de votre installation, n'oubliez pas d'interdire l'association après appairage.", - "scanButton": "Scanner", "serverNoResponse": "Gladys n'est pas accessible.", "serverNoResponseWebSocker": "Gladys n'est pas accessible. Veuillez actualiser la page.", "deviceNotHandled": "L'appareil n'est pas encore géré, veuillez nous contacter pour nous aider à le connecter dans Gladys !", diff --git a/front/src/routes/integration/all/zigbee2mqtt/discover-page/DiscoverTab.jsx b/front/src/routes/integration/all/zigbee2mqtt/discover-page/DiscoverTab.jsx index 0b8e0acf15..968c9246ab 100644 --- a/front/src/routes/integration/all/zigbee2mqtt/discover-page/DiscoverTab.jsx +++ b/front/src/routes/integration/all/zigbee2mqtt/discover-page/DiscoverTab.jsx @@ -45,15 +45,6 @@ const DiscoverTab = ({ children, ...props }) => ( -
- -
diff --git a/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js b/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js index fe1bf6f6da..2a49403f6e 100644 --- a/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js +++ b/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js @@ -2,42 +2,28 @@ import update from 'immutability-helper'; import createActionsIntegration from '../../../../../actions/integration'; import createActionsHouse from '../../../../../actions/house'; -let scanTimer; - function createActions(store) { const integrationActions = createActionsIntegration(store); const houseActions = createActionsHouse(store); const actions = { - async discover(state) { + async getDiscoveredDevices(state) { store.setState({ discoverZigbee2mqtt: true, discoverZigbee2mqttError: null }); - if (state.session.websocketConnected) { - try { - await state.httpClient.post('/api/v1/service/zigbee2mqtt/discover'); - scanTimer = setTimeout(store.setState, 5000, { - discoverZigbee2mqtt: false, - discoverZigbee2mqttError: null - }); - } catch (e) { - clearTimeout(scanTimer); - store.setState({ - zigbee2mqttDevices: [], - discoverZigbee2mqtt: false, - discoverZigbee2mqttError: 'integration.zigbee2mqtt.discover.serverNoResponse' - }); - } - } else { + + try { + const { devices: zigbee2mqttDevices } = await state.httpClient.get('/api/v1/service/zigbee2mqtt/devices'); + store.setState({ zigbee2mqttDevices, discoverZigbee2mqtt: false }); + } catch (e) { store.setState({ zigbee2mqttDevices: [], discoverZigbee2mqtt: false, - discoverZigbee2mqttError: 'integration.zigbee2mqtt.discover.serverNoResponseWebSocker' + discoverZigbee2mqttError: 'integration.zigbee2mqtt.discover.serverNoResponse' }); } }, setDiscoveredDevices(state, zigbee2mqttDevices) { - clearTimeout(scanTimer); store.setState({ zigbee2mqttDevices, discoverZigbee2mqtt: false, diff --git a/front/src/routes/integration/all/zigbee2mqtt/discover-page/index.js b/front/src/routes/integration/all/zigbee2mqtt/discover-page/index.js index a27d8ffcdc..ca8bf79c95 100644 --- a/front/src/routes/integration/all/zigbee2mqtt/discover-page/index.js +++ b/front/src/routes/integration/all/zigbee2mqtt/discover-page/index.js @@ -26,6 +26,7 @@ class Zigbee2mqttIntegration extends Component { this.props.setDiscoveredDevices(undefined); this.props.getHouses(); this.props.getIntegrationByName('zigbee2mqtt'); + this.props.getDiscoveredDevices(); } componentWillUnmount() { diff --git a/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js b/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js index ba37cceee2..8f5fbbc0cb 100644 --- a/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js +++ b/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js @@ -3,14 +3,14 @@ const logger = require('../../../utils/logger'); module.exports = function Zigbee2mqttController(gladys, zigbee2mqttManager) { /** - * @api {post} /api/v1/service/zigbee2mqtt/discover Launch Zigbee2mqtt devices discovery - * @apiName discover + * @api {get} /api/v1/service/zigbee2mqtt/devices Get discovered Zigbee2mqtt devices + * @apiName getDiscoveredDevices * @apiGroup Zigbee2mqtt */ - async function discover(req, res) { - logger.log('Launching devices discovery'); - await zigbee2mqttManager.discoverDevices(); - res.json({ status: 'discovering' }); + async function getDiscoveredDevices(req, res) { + logger.log('Get discovered devices'); + const devices = zigbee2mqttManager.getDiscoveredDevices(); + res.json({ devices }); } /** @@ -101,9 +101,9 @@ module.exports = function Zigbee2mqttController(gladys, zigbee2mqttManager) { } return { - 'post /api/v1/service/zigbee2mqtt/discover': { + 'get /api/v1/service/zigbee2mqtt/devices': { authenticated: true, - controller: asyncMiddleware(discover), + controller: asyncMiddleware(getDiscoveredDevices), }, 'get /api/v1/service/zigbee2mqtt/status': { authenticated: true, diff --git a/server/services/zigbee2mqtt/lib/discoverDevices.js b/server/services/zigbee2mqtt/lib/discoverDevices.js deleted file mode 100644 index 8f07c08b61..0000000000 --- a/server/services/zigbee2mqtt/lib/discoverDevices.js +++ /dev/null @@ -1,15 +0,0 @@ -const logger = require('../../../utils/logger'); - -/** - * @description Publish message on discover topic. - * @example - * discoverDevices(); - */ -function discoverDevices() { - logger.log('Ask for devices enumeration'); - this.mqttClient.publish('zigbee2mqtt/bridge/config/devices/get'); -} - -module.exports = { - discoverDevices, -}; diff --git a/server/services/zigbee2mqtt/lib/getDiscoveredDevices.js b/server/services/zigbee2mqtt/lib/getDiscoveredDevices.js new file mode 100644 index 0000000000..8f9d544d6c --- /dev/null +++ b/server/services/zigbee2mqtt/lib/getDiscoveredDevices.js @@ -0,0 +1,13 @@ +/** + * @description Get discovered devices. + * @returns {Array} Array of discovered devices. + * @example + * getDiscoveredDevices(); + */ +function getDiscoveredDevices() { + return this.discoveredDevices; +} + +module.exports = { + getDiscoveredDevices, +}; diff --git a/server/services/zigbee2mqtt/lib/handleMqttMessage.js b/server/services/zigbee2mqtt/lib/handleMqttMessage.js index 0e3a3a51a1..944ca85e8d 100644 --- a/server/services/zigbee2mqtt/lib/handleMqttMessage.js +++ b/server/services/zigbee2mqtt/lib/handleMqttMessage.js @@ -4,8 +4,6 @@ const { convertDevice } = require('../utils/convertDevice'); const { convertValue } = require('../utils/convertValue'); const { convertFeature } = require('../utils/convertFeature'); -const TYPE_COORDINATOR = 'Coordinator'; - /** * @description Handle a new message receive in MQTT. * @param {string} topic - MQTT topic. @@ -26,7 +24,7 @@ function handleMqttMessage(topic, message) { const devices = JSON.parse(message); const convertedDevices = devices // Remove Coordinator - .filter((d) => d.type !== TYPE_COORDINATOR) + .filter((d) => d.supported) // Remove existing devices .filter((d) => { const existingDevice = this.gladys.stateManager.get('deviceByExternalId', `zigbee2mqtt:${d.friendly_name}`); @@ -38,6 +36,8 @@ function handleMqttMessage(topic, message) { // Add features .map((d) => convertDevice(d, this.serviceId)); + this.discoveredDevices = convertedDevices; + this.gladys.event.emit(EVENTS.WEBSOCKET.SEND_ALL, { type: WEBSOCKET_MESSAGE_TYPES.ZIGBEE2MQTT.DISCOVER, payload: convertedDevices, diff --git a/server/services/zigbee2mqtt/lib/index.js b/server/services/zigbee2mqtt/lib/index.js index 8ff2c5a88e..329820060a 100644 --- a/server/services/zigbee2mqtt/lib/index.js +++ b/server/services/zigbee2mqtt/lib/index.js @@ -3,7 +3,7 @@ const { connect } = require('./connect'); const { getConfiguration } = require('./getConfiguration'); const { disconnect } = require('./disconnect'); const { handleMqttMessage } = require('./handleMqttMessage'); -const { discoverDevices } = require('./discoverDevices'); +const { getDiscoveredDevices } = require('./getDiscoveredDevices'); const { setValue } = require('./setValue'); const { status } = require('./status'); const { subscribe } = require('./subscribe'); @@ -27,6 +27,7 @@ const Zigbee2mqttManager = function Zigbee2mqttManager(gladys, mqttLibrary, serv this.serviceId = serviceId; this.mqttClient = null; + this.discoveredDevices = []; this.topicBinds = {}; this.usbConfigured = false; this.mqttExist = false; @@ -48,7 +49,7 @@ Zigbee2mqttManager.prototype.getConfiguration = getConfiguration; Zigbee2mqttManager.prototype.basePath = basePath; Zigbee2mqttManager.prototype.disconnect = disconnect; Zigbee2mqttManager.prototype.handleMqttMessage = handleMqttMessage; -Zigbee2mqttManager.prototype.discoverDevices = discoverDevices; +Zigbee2mqttManager.prototype.getDiscoveredDevices = getDiscoveredDevices; Zigbee2mqttManager.prototype.setValue = setValue; Zigbee2mqttManager.prototype.status = status; Zigbee2mqttManager.prototype.subscribe = subscribe; diff --git a/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js b/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js index 62efc2ff97..c49a886711 100644 --- a/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js +++ b/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js @@ -11,7 +11,7 @@ const gladys = { event, }; const zigbee2mqttManager = { - discoverDevices: fake.resolves(true), + getDiscoveredDevices: fake.returns(['device']), status: fake.returns(true), init: fake.returns(true), installMqttContainer: fake.returns(true), @@ -29,16 +29,16 @@ describe('zigbee2mqtt API', () => { sinon.reset(); }); - it('post /api/v1/service/zigbee2mqtt/discover', async () => { + it('get /api/v1/service/zigbee2mqtt/devices', async () => { const req = {}; const res = { json: fake.returns(null), }; - await controller['post /api/v1/service/zigbee2mqtt/discover'].controller(req, res); + await controller['get /api/v1/service/zigbee2mqtt/devices'].controller(req, res); - assert.calledOnce(zigbee2mqttManager.discoverDevices); - assert.calledWith(res.json, { status: 'discovering' }); + assert.calledOnce(zigbee2mqttManager.getDiscoveredDevices); + assert.calledWith(res.json, { devices: ['device'] }); }); it('get /api/v1/service/zigbee2mqtt/status', async () => { diff --git a/server/test/services/zigbee2mqtt/lib/discoverDevices.test.js b/server/test/services/zigbee2mqtt/lib/discoverDevices.test.js deleted file mode 100644 index 9851e06db1..0000000000 --- a/server/test/services/zigbee2mqtt/lib/discoverDevices.test.js +++ /dev/null @@ -1,27 +0,0 @@ -const sinon = require('sinon'); - -const { assert, fake } = sinon; -const proxyquire = require('proxyquire').noCallThru(); - -const Zigbee2MqttService = proxyquire('../../../../services/zigbee2mqtt', {}); - -const gladys = {}; - -const serviceId = 'f87b7af2-ca8e-44fc-b754-444354b42fee'; - -const mqtt = { - publish: fake.resolves(true), -}; - -describe('zigbee2mqtt discoverDevices', () => { - // PREPARE - const zigbee2MqttService = Zigbee2MqttService(gladys, serviceId); - zigbee2MqttService.device.mqttClient = mqtt; - - it('discover devices', async () => { - // EXECUTE - await zigbee2MqttService.device.discoverDevices(); - // ASSERT - assert.calledWith(mqtt.publish, 'zigbee2mqtt/bridge/config/devices/get'); - }); -}); diff --git a/server/test/services/zigbee2mqtt/lib/getDiscoveredDevices.test.js b/server/test/services/zigbee2mqtt/lib/getDiscoveredDevices.test.js new file mode 100644 index 0000000000..c338c4f256 --- /dev/null +++ b/server/test/services/zigbee2mqtt/lib/getDiscoveredDevices.test.js @@ -0,0 +1,18 @@ +const { expect } = require('chai'); + +const Zigbee2MqttService = require('../../../../services/zigbee2mqtt'); + +const gladys = {}; +const serviceId = 'f87b7af2-ca8e-44fc-b754-444354b42fee'; + +describe('zigbee2mqtt getDiscoveredDevices', () => { + // PREPARE + const zigbee2MqttService = Zigbee2MqttService(gladys, serviceId); + + it('get discovered devices', async () => { + // EXECUTE + const devices = zigbee2MqttService.device.getDiscoveredDevices(); + // ASSERT + expect(devices).deep.eq([]); + }); +}); From 49d76fdd2688dd0a9910ebe1901ce19eba917882 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Mon, 18 Oct 2021 21:39:07 +0200 Subject: [PATCH 4/9] Fix review comments --- .../all/zigbee2mqtt/discover-page/actions.js | 2 +- .../zigbee2mqtt/api/zigbee2mqtt.controller.js | 6 +-- .../zigbee2mqtt/exposes/binaryType.js | 20 --------- .../services/zigbee2mqtt/exposes/enumType.js | 22 ---------- .../zigbee2mqtt/exposes/numericType.js | 41 +------------------ .../zigbee2mqtt/utils/convertDevice.js | 1 - .../api/zigbee2mqtt.controller.test.js | 6 +-- 7 files changed, 9 insertions(+), 89 deletions(-) diff --git a/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js b/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js index 2a49403f6e..79859ca114 100644 --- a/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js +++ b/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js @@ -13,7 +13,7 @@ function createActions(store) { }); try { - const { devices: zigbee2mqttDevices } = await state.httpClient.get('/api/v1/service/zigbee2mqtt/devices'); + const zigbee2mqttDevices = await state.httpClient.get('/api/v1/service/zigbee2mqtt/device'); store.setState({ zigbee2mqttDevices, discoverZigbee2mqtt: false }); } catch (e) { store.setState({ diff --git a/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js b/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js index 8f5fbbc0cb..31a1e85915 100644 --- a/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js +++ b/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js @@ -3,14 +3,14 @@ const logger = require('../../../utils/logger'); module.exports = function Zigbee2mqttController(gladys, zigbee2mqttManager) { /** - * @api {get} /api/v1/service/zigbee2mqtt/devices Get discovered Zigbee2mqtt devices + * @api {get} /api/v1/service/zigbee2mqtt/device Get discovered Zigbee2mqtt devices * @apiName getDiscoveredDevices * @apiGroup Zigbee2mqtt */ async function getDiscoveredDevices(req, res) { logger.log('Get discovered devices'); const devices = zigbee2mqttManager.getDiscoveredDevices(); - res.json({ devices }); + res.json(devices); } /** @@ -101,7 +101,7 @@ module.exports = function Zigbee2mqttController(gladys, zigbee2mqttManager) { } return { - 'get /api/v1/service/zigbee2mqtt/devices': { + 'get /api/v1/service/zigbee2mqtt/device': { authenticated: true, controller: asyncMiddleware(getDiscoveredDevices), }, diff --git a/server/services/zigbee2mqtt/exposes/binaryType.js b/server/services/zigbee2mqtt/exposes/binaryType.js index 949c97db1a..df9d9dd7f6 100644 --- a/server/services/zigbee2mqtt/exposes/binaryType.js +++ b/server/services/zigbee2mqtt/exposes/binaryType.js @@ -9,26 +9,6 @@ module.exports = { max: 1, }, names: { - /* - auto_lock: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.SWITCH, - type: DEVICE_FEATURE_TYPES.SENSOR.BINARY, - }, - }, - away_mode: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.FAN, - type: DEVICE_FEATURE_TYPES.FAN.BINARY, - }, - }, - carbon_monoxide: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.MONOXIDE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - }, - }, - */ contact: { feature: { category: DEVICE_FEATURE_CATEGORIES.OPENING_SENSOR, diff --git a/server/services/zigbee2mqtt/exposes/enumType.js b/server/services/zigbee2mqtt/exposes/enumType.js index 1ddf759394..0cd27aef48 100644 --- a/server/services/zigbee2mqtt/exposes/enumType.js +++ b/server/services/zigbee2mqtt/exposes/enumType.js @@ -13,27 +13,5 @@ module.exports = { type: DEVICE_FEATURE_TYPES.BUTTON.CLICK, }, }, - /* - fan_mode: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.FAN, - type: DEVICE_FEATURE_TYPES.FAN.SPEED, - }, - }, - state: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.CURTAIN, - type: DEVICE_FEATURE_TYPES.CURTAIN.STATE, - min: 0, - max: 2, - }, - } - system_mode: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.FAN, - type: DEVICE_FEATURE_TYPES.FAN.MODE, - }, - }, - */ }, }; diff --git a/server/services/zigbee2mqtt/exposes/numericType.js b/server/services/zigbee2mqtt/exposes/numericType.js index 7d0b0e07dc..5212593530 100644 --- a/server/services/zigbee2mqtt/exposes/numericType.js +++ b/server/services/zigbee2mqtt/exposes/numericType.js @@ -41,12 +41,6 @@ module.exports = { max: 500, }, }, - /* - confort_temperature: { - feature: { - }, - }, - */ current: { feature: { category: DEVICE_FEATURE_CATEGORIES.SWITCH, @@ -76,7 +70,7 @@ module.exports = { }, cpu_temperature: { feature: { - category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, + category: DEVICE_FEATURE_CATEGORIES.DEVICE_TEMPERATURE_SENSOR, type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, unit: DEVICE_FEATURE_UNITS.CELSIUS, min: -100, @@ -85,7 +79,7 @@ module.exports = { }, device_temperature: { feature: { - category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, + category: DEVICE_FEATURE_CATEGORIES.DEVICE_TEMPERATURE_SENSOR, type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, unit: DEVICE_FEATURE_UNITS.CELSIUS, min: -100, @@ -118,16 +112,6 @@ module.exports = { type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, }, }, - /* - hue: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT, - type: DEVICE_FEATURE_TYPES.LIGHT.HUE, - min: 150?, - max: 500?, - }, - }, - */ humidity: { feature: { category: DEVICE_FEATURE_CATEGORIES.HUMIDITY_SENSOR, @@ -163,17 +147,6 @@ module.exports = { max: 150, }, }, - /* - position: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.CURTAIN, - type: DEVICE_FEATURE_TYPES.CURTAIN.POSITION, - unit: DEVICE_FEATURE_UNITS.PERCENT, - min: 0, - max: 100, - }, - }, - */ power: { feature: { category: DEVICE_FEATURE_CATEGORIES.SWITCH, @@ -188,16 +161,6 @@ module.exports = { unit: DEVICE_FEATURE_UNITS.HECTO_PASCAL, }, }, - /* - saturation: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.LIGHT, - type: DEVICE_FEATURE_TYPES.LIGHT.SATURATION, - min: 150?, - max: 500?, - }, - }, - */ temperature: { feature: { category: DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR, diff --git a/server/services/zigbee2mqtt/utils/convertDevice.js b/server/services/zigbee2mqtt/utils/convertDevice.js index 7f3afe0836..375c1da7d4 100644 --- a/server/services/zigbee2mqtt/utils/convertDevice.js +++ b/server/services/zigbee2mqtt/utils/convertDevice.js @@ -29,7 +29,6 @@ function convertDevice(device, serviceId) { }; logger.debug(`Device ${name} / model ${model} ${supported ? '' : 'NOT'} managed by Gladys`); - logger.debug(gladysDevice); return gladysDevice; } diff --git a/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js b/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js index c49a886711..8397994f81 100644 --- a/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js +++ b/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js @@ -29,16 +29,16 @@ describe('zigbee2mqtt API', () => { sinon.reset(); }); - it('get /api/v1/service/zigbee2mqtt/devices', async () => { + it('get /api/v1/service/zigbee2mqtt/device', async () => { const req = {}; const res = { json: fake.returns(null), }; - await controller['get /api/v1/service/zigbee2mqtt/devices'].controller(req, res); + await controller['get /api/v1/service/zigbee2mqtt/device'].controller(req, res); assert.calledOnce(zigbee2mqttManager.getDiscoveredDevices); - assert.calledWith(res.json, { devices: ['device'] }); + assert.calledWith(res.json, ['device']); }); it('get /api/v1/service/zigbee2mqtt/status', async () => { From f7fd24896508c3adb666745af16d5b039f74135e Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Thu, 21 Oct 2021 20:59:36 +0200 Subject: [PATCH 5/9] Load composite type --- server/services/zigbee2mqtt/exposes/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/services/zigbee2mqtt/exposes/index.js b/server/services/zigbee2mqtt/exposes/index.js index 6e5e303293..47088da97b 100644 --- a/server/services/zigbee2mqtt/exposes/index.js +++ b/server/services/zigbee2mqtt/exposes/index.js @@ -1,9 +1,11 @@ const binaryType = require('./binaryType'); const numericType = require('./numericType'); const enumType = require('./enumType'); +const compositeType = require('./compositeType'); module.exports = { [binaryType.type]: binaryType, [numericType.type]: numericType, [enumType.type]: enumType, + [compositeType.type]: compositeType, }; From e3d90bdbebbd6174c84eefef44452ecdd23d3426 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Thu, 21 Oct 2021 21:04:41 +0200 Subject: [PATCH 6/9] Illuminance max value --- server/services/zigbee2mqtt/exposes/numericType.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/services/zigbee2mqtt/exposes/numericType.js b/server/services/zigbee2mqtt/exposes/numericType.js index 5212593530..ce2cd9b9bd 100644 --- a/server/services/zigbee2mqtt/exposes/numericType.js +++ b/server/services/zigbee2mqtt/exposes/numericType.js @@ -126,7 +126,7 @@ module.exports = { category: DEVICE_FEATURE_CATEGORIES.LIGHT_SENSOR, type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, min: 0, - max: 10000, + max: 100000, }, }, illuminance_lux: { @@ -135,7 +135,7 @@ module.exports = { type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, unit: DEVICE_FEATURE_UNITS.LUX, min: 0, - max: 10000, + max: 100000, }, }, local_temperature: { From 0e2cfdfc28c9a6cf58ee568f1a7a5b3342daf45b Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Sat, 23 Oct 2021 08:21:27 +0200 Subject: [PATCH 7/9] Force composite color as actuator --- server/services/zigbee2mqtt/exposes/compositeType.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/services/zigbee2mqtt/exposes/compositeType.js b/server/services/zigbee2mqtt/exposes/compositeType.js index 2db3ea9adc..82bc42b1f7 100644 --- a/server/services/zigbee2mqtt/exposes/compositeType.js +++ b/server/services/zigbee2mqtt/exposes/compositeType.js @@ -7,6 +7,8 @@ module.exports = { feature: { category: DEVICE_FEATURE_CATEGORIES.LIGHT, type: DEVICE_FEATURE_TYPES.LIGHT.COLOR, + has_feedback: true, + read_only: false, min: 0, max: 16777215, }, From ce829792acdb63384d926d0654973cf61f6e3b50 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Sun, 24 Oct 2021 09:12:19 +0200 Subject: [PATCH 8/9] Fix zigbee2mqtt discovered path --- front/src/config/demo.json | 2175 +++++++++++++++++ .../all/zigbee2mqtt/discover-page/actions.js | 2 +- .../zigbee2mqtt/api/zigbee2mqtt.controller.js | 4 +- .../api/zigbee2mqtt.controller.test.js | 4 +- 4 files changed, 2180 insertions(+), 5 deletions(-) create mode 100644 front/src/config/demo.json diff --git a/front/src/config/demo.json b/front/src/config/demo.json new file mode 100644 index 0000000000..0273dfff32 --- /dev/null +++ b/front/src/config/demo.json @@ -0,0 +1,2175 @@ +{ + "post /api/v1/login": { + "id": "215811c9-c0aa-4148-8a4b-e02892d7446f", + "firstname": "tony", + "lastname": "Stark", + "email": "tony.stark@gladysassistant.com", + "language": "en", + "birthdate": "2011-02-04", + "role": "admin", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z", + "refresh_token": "15535ed55088d46b9a01738bfb2b96f982fb16edb2a5241d078775a7db8aa38a8ae59e73f81aa5367b62b1daef8aea5e3b7de4ff66dc8fb00f6ed02b6c3eb14ac68b1716e9cdb9425f88bf2eeb5b8cc3b4eb66913bbd8e5084381dc22fe1ff092c0efd80f2ec766511f03121bdffcc02202a20d5916e6e58c6aed4a84fb9980a99b828c8ded74d17e3c91108f7e50dccb80281720b6b37fe26345371cd2b4a1134abfbc63689814375aee968af15dc24379c7c95200c0c1740817806abfca934ccb4fb183e4c95e19f55a2e4c8a3bb453cf0700a6f7baa7088b24297d212f2ccfc3586093c28e731e9909addbead2b9c095f1a7f8993f4ddd405", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTJiZjk0NDEtYzljMC00YTVmLWI3YmItNGY3NmYwZWM0Yzk1Iiwic2NvcGUiOlsiZGFzaGJvYXJkOndyaXRlIiwiZGFzaGJvYXJkOnJlYWQiXSwic2Vzc2lvbl9pZCI6IjZhOTYyNzk2LTZlMGQtNDRiNC04Y2Y2LWRkMmJhYjhjY2M0ZiIsImlhdCI6MTU1MTA2NzM5MywiZXhwIjoxNTUxMTUzNzkzLCJhdWQiOiJ1c2VyIiwiaXNzIjoiZ2xhZHlzIn0.JfiRsTn4cyARIMElD5DgyFt7xKHPcTNnaMLKznbfVc4" + }, + "get /api/v1/me": { + "id": "215811c9-c0aa-4148-8a4b-e02892d7446f", + "firstname": "Tony", + "lastname": "Stark", + "selector": "tony", + "email": "tony.stark@gladysassistant.com", + "language": "en", + "birthdate": "2011-02-04", + "role": "admin", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z", + "refresh_token": "15535ed55088d46b9a01738bfb2b96f982fb16edb2a5241d078775a7db8aa38a8ae59e73f81aa5367b62b1daef8aea5e3b7de4ff66dc8fb00f6ed02b6c3eb14ac68b1716e9cdb9425f88bf2eeb5b8cc3b4eb66913bbd8e5084381dc22fe1ff092c0efd80f2ec766511f03121bdffcc02202a20d5916e6e58c6aed4a84fb9980a99b828c8ded74d17e3c91108f7e50dccb80281720b6b37fe26345371cd2b4a1134abfbc63689814375aee968af15dc24379c7c95200c0c1740817806abfca934ccb4fb183e4c95e19f55a2e4c8a3bb453cf0700a6f7baa7088b24297d212f2ccfc3586093c28e731e9909addbead2b9c095f1a7f8993f4ddd405", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTJiZjk0NDEtYzljMC00YTVmLWI3YmItNGY3NmYwZWM0Yzk1Iiwic2NvcGUiOlsiZGFzaGJvYXJkOndyaXRlIiwiZGFzaGJvYXJkOnJlYWQiXSwic2Vzc2lvbl9pZCI6IjZhOTYyNzk2LTZlMGQtNDRiNC04Y2Y2LWRkMmJhYjhjY2M0ZiIsImlhdCI6MTU1MTA2NzM5MywiZXhwIjoxNTUxMTUzNzkzLCJhdWQiOiJ1c2VyIiwiaXNzIjoiZ2xhZHlzIn0.JfiRsTn4cyARIMElD5DgyFt7xKHPcTNnaMLKznbfVc4" + }, + "get /api/v1/me/picture": "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QN3aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzEzOCA3OS4xNTk4MjQsIDIwMTYvMDkvMTQtMDE6MDk6MDEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MTI2MGJlNWQtZGNmNS00ZjQ4LWFlMDktMGEyMmMyMTk5ODRhIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjA1OEYwOUNGNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjA1OEYwOUNFNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE3IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjhENDY0NEYzRjU2MTExRTZCRDFDRjlEQkVEMTk5NEU3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhENDY0NEY0RjU2MTExRTZCRDFDRjlEQkVEMTk5NEU3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpS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", + "get /api/v1/user?fields=id,firstname,selector,picture,last_latitude,last_longitude,last_altitude,last_accuracy,last_location_changed": [ + { + "firstname": "Tony", + "last_latitude": 41.93425385676557, + "last_longitude": 12.402756238310928, + "picture": "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QN3aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzEzOCA3OS4xNTk4MjQsIDIwMTYvMDkvMTQtMDE6MDk6MDEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MTI2MGJlNWQtZGNmNS00ZjQ4LWFlMDktMGEyMmMyMTk5ODRhIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjA1OEYwOUNGNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjA1OEYwOUNFNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE3IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjhENDY0NEYzRjU2MTExRTZCRDFDRjlEQkVEMTk5NEU3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhENDY0NEY0RjU2MTExRTZCRDFDRjlEQkVEMTk5NEU3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpS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" + } + ], + "post /api/v1/access-token": { + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTJiZjk0NDEtYzljMC00YTVmLWI3YmItNGY3NmYwZWM0Yzk1Iiwic2NvcGUiOlsiZGFzaGJvYXJkOndyaXRlIiwiZGFzaGJvYXJkOnJlYWQiXSwic2Vzc2lvbl9pZCI6IjZhOTYyNzk2LTZlMGQtNDRiNC04Y2Y2LWRkMmJhYjhjY2M0ZiIsImlhdCI6MTU1MTA2NzM5MywiZXhwIjoxNTUxMTUzNzkzLCJhdWQiOiJ1c2VyIiwiaXNzIjoiZ2xhZHlzIn0.JfiRsTn4cyARIMElD5DgyFt7xKHPcTNnaMLKznbfVc4" + }, + "post /api/v1/user": { + "id": "215811c9-c0aa-4148-8a4b-e02892d7446f", + "firstname": "tony", + "lastname": "Stark", + "email": "tony.stark@gladysassistant.com", + "language": "en", + "birthdate": "2011-02-04", + "role": "admin", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z", + "refresh_token": "15535ed55088d46b9a01738bfb2b96f982fb16edb2a5241d078775a7db8aa38a8ae59e73f81aa5367b62b1daef8aea5e3b7de4ff66dc8fb00f6ed02b6c3eb14ac68b1716e9cdb9425f88bf2eeb5b8cc3b4eb66913bbd8e5084381dc22fe1ff092c0efd80f2ec766511f03121bdffcc02202a20d5916e6e58c6aed4a84fb9980a99b828c8ded74d17e3c91108f7e50dccb80281720b6b37fe26345371cd2b4a1134abfbc63689814375aee968af15dc24379c7c95200c0c1740817806abfca934ccb4fb183e4c95e19f55a2e4c8a3bb453cf0700a6f7baa7088b24297d212f2ccfc3586093c28e731e9909addbead2b9c095f1a7f8993f4ddd405", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTJiZjk0NDEtYzljMC00YTVmLWI3YmItNGY3NmYwZWM0Yzk1Iiwic2NvcGUiOlsiZGFzaGJvYXJkOndyaXRlIiwiZGFzaGJvYXJkOnJlYWQiXSwic2Vzc2lvbl9pZCI6IjZhOTYyNzk2LTZlMGQtNDRiNC04Y2Y2LWRkMmJhYjhjY2M0ZiIsImlhdCI6MTU1MTA2NzM5MywiZXhwIjoxNTUxMTUzNzkzLCJhdWQiOiJ1c2VyIiwiaXNzIjoiZ2xhZHlzIn0.JfiRsTn4cyARIMElD5DgyFt7xKHPcTNnaMLKznbfVc4" + }, + "patch /api/v1/user": { + "id": "215811c9-c0aa-4148-8a4b-e02892d7446f", + "firstname": "tony", + "lastname": "Stark", + "email": "tony.stark@gladysassistant.com", + "language": "en", + "birthdate": "2011-02-04", + "role": "admin", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z", + "refresh_token": "15535ed55088d46b9a01738bfb2b96f982fb16edb2a5241d078775a7db8aa38a8ae59e73f81aa5367b62b1daef8aea5e3b7de4ff66dc8fb00f6ed02b6c3eb14ac68b1716e9cdb9425f88bf2eeb5b8cc3b4eb66913bbd8e5084381dc22fe1ff092c0efd80f2ec766511f03121bdffcc02202a20d5916e6e58c6aed4a84fb9980a99b828c8ded74d17e3c91108f7e50dccb80281720b6b37fe26345371cd2b4a1134abfbc63689814375aee968af15dc24379c7c95200c0c1740817806abfca934ccb4fb183e4c95e19f55a2e4c8a3bb453cf0700a6f7baa7088b24297d212f2ccfc3586093c28e731e9909addbead2b9c095f1a7f8993f4ddd405", + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTJiZjk0NDEtYzljMC00YTVmLWI3YmItNGY3NmYwZWM0Yzk1Iiwic2NvcGUiOlsiZGFzaGJvYXJkOndyaXRlIiwiZGFzaGJvYXJkOnJlYWQiXSwic2Vzc2lvbl9pZCI6IjZhOTYyNzk2LTZlMGQtNDRiNC04Y2Y2LWRkMmJhYjhjY2M0ZiIsImlhdCI6MTU1MTA2NzM5MywiZXhwIjoxNTUxMTUzNzkzLCJhdWQiOiJ1c2VyIiwiaXNzIjoiZ2xhZHlzIn0.JfiRsTn4cyARIMElD5DgyFt7xKHPcTNnaMLKznbfVc4" + }, + "get /api/v1/user?fields=firstname,lastname,role,selector,picture,current_house_id,last_house_changed": [ + { + "firstname": "Tony", + "lastname": "Stark", + "role": "admin", + "selector": "tony", + "picture": "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QN3aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzEzOCA3OS4xNTk4MjQsIDIwMTYvMDkvMTQtMDE6MDk6MDEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MTI2MGJlNWQtZGNmNS00ZjQ4LWFlMDktMGEyMmMyMTk5ODRhIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjA1OEYwOUNGNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjA1OEYwOUNFNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE3IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjhENDY0NEYzRjU2MTExRTZCRDFDRjlEQkVEMTk5NEU3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhENDY0NEY0RjU2MTExRTZCRDFDRjlEQkVEMTk5NEU3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpS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", + "current_house_id": "8fe7acf2-f27b-46d4-9f8e-c871ab1e6780", + "last_house_changed": "2021-07-12T07:22:19.014Z" + }, + { + "firstname": "Pepper", + "lastname": "Pots", + "role": "admin", + "selector": "pepper", + "picture": "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMfaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzEzOCA3OS4xNTk4MjQsIDIwMTYvMDkvMTQtMDE6MDk6MDEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjA1OEYwOUQzNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjA1OEYwOUQyNzU0NzExRTk4QUQyQjRBMzgwQkI2MUUwIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE3IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSIxRDA4OTdGQUMxQ0RFMEFBMzRFMTMwQzJBOUY0OUEzRiIgc3RSZWY6ZG9jdW1lbnRJRD0iMUQwODk3RkFDMUNERTBBQTM0RTEzMEMyQTlGNDlBM0YiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwM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", + "current_house_id": "", + "last_house_changed": "2021-07-12T07:22:19.014Z" + } + ], + "get /api/v1/dashboard": [ + { + "id": "329897d2-0620-458c-addf-4009ff5bc205", + "name": "Home", + "type": "main", + "selector": "home" + } + ], + "get /api/v1/dashboard/home": { + "id": "329897d2-0620-458c-addf-4009ff5bc205", + "name": "Home", + "type": "main", + "selector": "home", + "boxes": [ + [ + { + "type": "weather", + "house": "main-house" + }, + { + "type": "camera", + "camera": "living-room-camera", + "name": "Garden" + } + ], + [ + { + "type": "temperature-in-room", + "room": "living-room" + }, + { + "type": "devices-in-room", + "room": "living-room", + "device_features": [ + "main-lamp-binary", + "tv-lamp-binary", + "tv-lamp-color", + "tv-lamp-brightness", + "mqtt-living-room-switch", + "mqtt-living-room-dimmer", + "mqtt-living-room-temp" + ] + } + ], + [ + { + "type": "devices-in-room", + "room": "kitchen", + "device_features": ["main-presence-sensor"] + }, + { + "type": "user-presence" + } + ] + ], + "created_at": "2019-05-15T08:48:20.275Z", + "updated_at": "2019-05-16T06:29:44.767Z" + }, + "patch /api/v1/dashboard/home": { + "id": "329897d2-0620-458c-addf-4009ff5bc205", + "name": "Home", + "type": "main", + "selector": "home", + "boxes": [ + [ + { + "type": "weather", + "house": "main-house" + }, + { + "type": "camera", + "camera": "living-room-camera", + "name": "Garden" + } + ], + [ + { + "type": "temperature-in-room", + "room": "living-room" + }, + { + "type": "user-presence" + }, + { + "type": "devices-in-room", + "room": "living-room" + } + ], + [ + { + "type": "devices-in-room", + "room": "kitchen" + } + ] + ], + "created_at": "2019-05-15T08:48:20.275Z", + "updated_at": "2019-05-16T06:29:44.767Z" + }, + "get /api/v1/house/main-house/weather": { + "temperature": 27.9, + "humidity": 0.99, + "pressure": 1005.09, + "datetime": "2019-05-09T04:27:57.000Z", + "units": "metric", + "wind_speed": 1.96, + "weather": "rain", + "house": { + "id": "6a29f33b-e5c9-4b08-9d3f-ced2cab80a87", + "name": "Main house", + "selector": "main-house", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z" + }, + "options": { + "latitude": 12, + "longitude": 12, + "language": "en" + }, + "hours": [ + { + "temperature": 27.9, + "humidity": 0.99, + "pressure": 1005.09, + "datetime": "2019-05-09T04:27:57.000Z", + "units": "metric", + "wind_speed": 1.96, + "wind_direction": 1.96, + "weather": "rain" + } + ], + "days": [ + { + "temperature_min": 20.9, + "temperature_max": 27.9, + "humidity": 0.99, + "pressure": 1005.09, + "datetime": "2019-05-09T04:27:57.000Z", + "units": "metric", + "wind_speed": 1.96, + "wind_direction": 1.96, + "weather": "rain" + } + ] + }, + "get /api/v1/room/living-room?expand=temperature": { + "id": "1c634ff4-0476-4733-a084-b4a43d649c84", + "name": "Living Room", + "selector": "living-room", + "temperature": { + "temperature": 26.5, + "unit": "celsius" + } + }, + "get /api/v1/camera/living-room-camera/image": "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA1AAD/4QMJaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzEzOCA3OS4xNTk4MjQsIDIwMTYvMDkvMTQtMDE6MDk6MDEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjRFMTgwODcwNzU0MjExRTk5Nzc5RkZBMTY3OTgyRDBEIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjRFMTgwODZGNzU0MjExRTk5Nzc5RkZBMTY3OTgyRDBEIiB4bXA6Q3JlYXRvclRvb2w9IlZlci4xLjAuMDAwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9IkYzN0JCRDNCMzkwRTdEQ0NEMkQ5ODI4MDFGNTkwMTZBIiBzdFJlZjpkb2N1bWVudElEPSJGMzdCQkQzQjM5MEU3RENDRDJEOTgyODAxRjU5MDE2QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/tAEhQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAADxwBWgADGyVHHAIAAAIAAgA4QklNBCUAAAAAABD84R+JyLfJeC80YjQHWHfr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mUgIL2HDcptx537RXyPsbduJzXs011UM7MizJx6zmRtwWN4zqF5buI48a4bdu/nP+WppFFHhxAViViAfPc7XbcOFtNOym2l7JnwS+qGaZnjSI3VV1UjVAdTt8vPgKa6zz8nlVy+s5674k8qtY70J2gXsWsON++t9f19PKXaoMSdJXsyqrEbEY6gNb4mGpPZXXfXEZlNfp+fPneiVLynU3IO0ct2psK69W0s4Tb9XS9O6ZFgxWB3ysPPIefcOwV2kc7VlmABJ0A4mqyz8jMeV/Sgub8TUtawmxen7fNJq1JEtPy8jHxEu583JRxrSMPKz5MhuxeQFFw9ARNK9jzJVSqJAlA4LQHbVB20UttAdtAttAttQLbQHbQLbQLbRR20C20B20C20B21AttAdtAttAdtAttFHbRB20UttAdtAttAttAdtELbQHbUC20C20C20Acqi7mNgOdZ22kma1JmuK+o3xcrPMfpWaJQLptUE637eBr43b2bbbZ8PbprJMObyMibFbZFLtYteQjmp107q1JN/MTOFoT408AlVHaS/AG9ral728puNBXHaevDXnkmzMNg2R+4Utdo18ouQNfLY+81j038cLMeVKdlzGX5dP2pHALPzYDdtL2sLc666/s83lm8pB9N5hlURMhhfWQ6+Q+77q30d38nH4Z2mG/h4EGFEEj1NhvdviYjmf4V7JMOVuT5po4l3ObD761lGcWyM59qDbCDxqZXDRxcKKBL2AtqWNWapazeqfUUMF4cTzyc35DwqkjAfJlncvIxZjzNFPU1B6kiV7XmSqlEPC1Q4LRR20C20B21QttAttAdtQLbQLbQLbUB20UdtAttAdtAttAttAdtAttAdtQHbQLbVUdtQLZRB20B20C20UttEHZQLZQHbRS21ELbVC21BjfUz5sUCNj3EZO1yNtrntD8fZXl+xrvcert1WfLleodVeMOzYyDITdGZVPmsLhdOAFr614tr41+Y9Enz8OZRhkTmSclkj1Y3F72v7tK1tPWcJOUCZTpklVnMSX+NQe46DTUV0mks5T2pkmS4ILMxTduZQdu7ne9udT0/BltdMxuo5sscin08Jk2yk/mPD4f1W51x16Zc/wBWrvY6SOJIo1jTREFgCb/aa9OusnhytyrZubFji3xSclH41rKYVIcPIzJBLk3CHgndSTK24XcnJwemwb5mCgDy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", + "get /api/v1/room/living-room?expand=devices": { + "id": "1c634ff4-0476-4733-a084-b4a43d649c84", + "name": "Living Room", + "selector": "living-room", + "devices": [ + { + "id": "b32daa9a-8f77-4394-b4f3-ffea215062d2", + "name": "Main Lamp", + "selector": "main-lamp", + "features": [ + { + "name": "Main Lamp", + "selector": "main-lamp-binary", + "category": "light", + "type": "binary", + "min": 0, + "max": 1, + "read_only": false, + "last_value": 1, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "TV Lamp color", + "selector": "tv-lamp-color", + "category": "light", + "type": "color", + "min": 0, + "max": 16777215, + "read_only": false, + "last_value": 65000, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "TV Lamp brightness", + "selector": "tv-lamp-brightness", + "category": "light", + "type": "brightness", + "min": 0, + "max": 100, + "read_only": false, + "last_value": 55, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + }, + { + "id": "b32daa9a-8f77-4394-b4f3-ffea215062d2", + "name": "TV Lamp", + "selector": "tv-lamp", + "features": [ + { + "name": "TV Lamp feature", + "selector": "tv-lamp-binary", + "category": "light", + "type": "binary", + "min": 0, + "max": 1, + "read_only": false, + "last_value": 1, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + }, + { + "id": "adefb484-223e-478a-8330-8fb1b3a20920", + "selector": "temperature-living-room", + "features": [ + { + "name": "Temperature", + "selector": "temperature-living-room-celsius", + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "min": -200, + "max": 200, + "read_only": true, + "last_value": 27, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + }, + { + "id": "81d637d2-b7f5-4cc3-a39e-2270fd069ee2", + "selector": "mqtt-living-room", + "name": "MQTT device", + "service": { + "name": "mqtt" + }, + "features": [ + { + "name": "Window Temp", + "selector": "mqtt-living-room-temp", + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "min": -200, + "max": 200, + "read_only": true, + "last_value": 27, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + } + ] + }, + "get /api/v1/room/kitchen?expand=devices": { + "id": "be6ba391-ebb3-472d-81af-d75d710a8430", + "name": "Kitchen", + "selector": "kitchen", + "devices": [ + { + "id": "adefb484-223e-478a-8330-8fb1b3a20920", + "selector": "sensor-kitchen", + "features": [ + { + "name": "Temperature", + "selector": "temperature-living-room-celsius", + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "min": -200, + "max": 200, + "read_only": true, + "last_value": 30, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "Humidity", + "selector": "temperature-living-room-celsius", + "category": "humidity-sensor", + "type": "decimal", + "unit": "percent", + "min": -200, + "max": 200, + "read_only": true, + "last_value": 70, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "Co2", + "selector": "co2-kitchen", + "category": "co2-sensor", + "type": "decimal", + "unit": "ppm", + "min": 0, + "max": 5000, + "read_only": true, + "last_value": 340, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "Presence", + "selector": "main-presence-sensor", + "category": "presence-sensor", + "type": "push", + "unit": null, + "min": 0, + "max": 1, + "read_only": true, + "last_value": 0, + "last_value_changed": "2021-07-13 13:49:07.556 +01:00" + }, + { + "name": "Kitchen door", + "selector": "temperature-living-room-celsius", + "category": "opening-sensor", + "type": "binary", + "unit": null, + "min": -200, + "max": 200, + "read_only": true, + "last_value": 0, + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + } + ] + }, + "post /api/v1/variable/DEVICE_STATE_HISTORY_IN_DAYS": { + "id": "18da1930-abe9-4c99-ab9c-7ddd61aef692", + "name": "DEVICE_STATE_HISTORY_IN_DAYS", + "value": 90, + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z" + }, + "post /api/v1/house": { + "id": "6a29f33b-e5c9-4b08-9d3f-ced2cab80a87", + "name": "My House", + "selector": "my-house", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z" + }, + "post /api/v1/house/my-house/room": { + "id": "1bdc3614-6082-43c3-9e4a-3b00781013a4", + "name": "My room", + "house_id": "6a29f33b-e5c9-4b08-9d3f-ced2cab80a87", + "created_at": "2019-02-20T04:26:47.811Z", + "updated_at": "2019-02-20T04:26:47.811Z" + }, + "get /api/v1/room?expand=devices": [ + { + "id": "1c634ff4-0476-4733-a084-b4a43d649c84", + "name": "Living Room", + "selector": "living-room", + "devices": [ + { + "id": "b32daa9a-8f77-4394-b4f3-ffea215062d2", + "name": "Multi-sensor", + "selector": "sensors", + "features": [ + { + "name": "Temperature", + "selector": "temperature-sensor", + "category": "temperature-sensor", + "type": "decimal", + "min": -20, + "max": 255, + "read_only": true, + "last_value": 25, + "unit": "celsius", + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "Humidity", + "selector": "humidity-sensor", + "category": "humidity-sensor", + "type": "decimal", + "min": 0, + "max": 100, + "read_only": true, + "last_value": 56, + "unit": "percent", + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "Co2", + "selector": "co2-sensor", + "category": "co2-sensor", + "type": "decimal", + "min": 0, + "max": 5000, + "read_only": true, + "last_value": 410, + "unit": "ppm", + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + }, + { + "name": "Door", + "selector": "door-opening-sensor", + "category": "door-opening-sensor", + "type": "binary", + "min": 0, + "max": 1, + "read_only": true, + "last_value": 0, + "unit": "percent", + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + } + ] + }, + { + "id": "ab42585c-415d-4696-8f4c-ff0283dcb954", + "name": "Kitchen", + "selector": "kitchen", + "devices": [ + { + "id": "b32daa9a-8f77-4394-b4f3-ffea215062d2", + "name": "Kitchen Light", + "selector": "sensors", + "features": [ + { + "name": "Light", + "selector": "main-lamp-binary", + "category": "light", + "type": "binary", + "min": 0, + "max": 100, + "read_only": true, + "last_value": 60, + "unit": " lux", + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + }, + { + "id": "284d8f68-220c-45fd-a73a-eccb547aff24", + "name": "Sensor", + "selector": "humidity-sensor", + "features": [ + { + "name": "Humidity", + "selector": "kitchen-humidity-sensor", + "category": "humidity-sensor", + "type": "decimal", + "min": 0, + "max": 100, + "read_only": true, + "last_value": 74, + "unit": "percent", + "last_value_changed": "2019-02-12 07:49:07.556 +00:00" + } + ] + } + ] + } + ], + "post /api/v1/light/main-lamp/on": { + "type": "light.turn-on", + "device": "main-lamp", + "status": "pending" + }, + "get /api/v1/service/philips-hue/bridge": [ + { + "name": "Philips hue", + "ipaddress": "192.168.2.245" + } + ], + "get /api/v1/message": [ + { + "id": "247b1dd0-6fab-47a8-a9c8-1405deae0ae8", + "sender_id": null, + "receiver_id": "0cd30aef-9c4e-4a23-88e3-3547971296e5", + "text": "It's a clear day today. Temperature outside is 26°C.", + "is_read": true, + "created_at": "2019-02-12T07:45:07.556Z" + }, + { + "id": "247b1dd0-6fab-47a8-a9c8-1405deae0ae8", + "sender_id": "0cd30aef-9c4e-4a23-88e3-3547971296e5", + "receiver_id": null, + "text": "What's the weather like?", + "is_read": true, + "created_at": "2019-02-12T07:44:07.556Z" + }, + { + "id": "247b1dd0-6fab-47a8-a9c8-1405deae0ae8", + "sender_id": null, + "receiver_id": "0cd30aef-9c4e-4a23-88e3-3547971296e5", + "text": "It's 24°C in the kitchen.", + "is_read": true, + "created_at": "2019-02-12T07:49:07.557Z" + }, + { + "id": "247b1dd0-6fab-47a8-a9c8-1405deae0ae8", + "sender_id": "0cd30aef-9c4e-4a23-88e3-3547971296e5", + "receiver_id": null, + "text": "What's the temperature in the kitchen?", + "is_read": true, + "created_at": "2019-02-12T07:49:07.556Z" + } + ], + "post /api/v1/message": { + "id": "247b1dd0-6fab-47a8-a9c8-1405deae0ae8", + "sender_id": "0cd30aef-9c4e-4a23-88e3-3547971296e5", + "receiver_id": null, + "text": "What time is it ?", + "is_read": true, + "created_at": "2019-02-12T07:49:07.556Z" + }, + "get /api/v1/scene": [ + { + "id": "5f515235-2a00-45f7-993f-cb24b463feec", + "selector": "wake-up", + "icon": "fe fe-bell", + "active": true, + "name": "Wake Up", + "description": "Tony's wake up scene" + } + ], + "get /api/v1/user": [ + { + "id": "d84ced32-d937-4cf6-a32e-105ffb584226", + "firstname": "Tony", + "lastname": "Stark", + "selector": "tony", + "role": "admin" + }, + { + "id": "2a16e6bb-34a8-46b9-90d3-275e4d059b41", + "firstname": "Pepper", + "lastname": "Pots", + "selector": "pepper", + "role": "user" + } + ], + "get /api/v1/user/tony": { + "id": "d84ced32-d937-4cf6-a32e-105ffb584226", + "firstname": "Tony", + "lastname": "Stark", + "selector": "tony", + "email": "tony.stark@gladysassistant.com", + "birthdate": "2011-02-04", + "language": "en", + "role": "admin" + }, + "get /api/v1/user/pepper": { + "id": "d84ced32-d937-4cf6-a32e-105ffb584226", + "firstname": "Pepper", + "lastname": "Pots", + "selector": "pepper", + "email": "pepper.pots@gladysassistant.com", + "birthdate": "2011-02-04", + "language": "en", + "role": "admin" + }, + "get /api/v1/scene/wake-up": { + "id": "5f515235-2a00-45f7-993f-cb24b463feec", + "selector": "wake-up", + "icon": "fe fe-bell", + "active": true, + "name": "Wake Up", + "triggers": [ + { + "type": "device.new-state", + "device_feature": "main-lamp-binary", + "operator": "=", + "value": 1 + } + ], + "actions": [ + [ + { + "type": "delay", + "value": 2, + "unit": "seconds" + } + ], + [ + { + "type": "light.turn-on", + "devices": ["light"] + } + ] + ] + }, + "get /api/v1/service/zwave/node": [ + { + "name": "ZME_UZB1 USB Stick", + "features": [], + "params": [], + "rawZwaveNode": { + "id": 1, + "manufacturer": "Z-Wave.Me", + "manufacturerid": "0x0115", + "product": "ZME_UZB1 USB Stick", + "producttype": "0x0400", + "productid": "0x0001", + "type": "Static PC Controller" + } + } + ], + "get /api/v1/service/zwave/neighbor": [ + { + "id": "1", + "manufacturer": "Z-Wave.Me", + "product": "ZME_UZB1 USB Stick", + "neighbors": [2, 3, 4, 5, 6, 7, 8, 10] + }, + { + "id": "2", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "3", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "4", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "5", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "6", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "7", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "8", + "manufacturer": "", + "product": "", + "neighbors": [] + }, + { + "id": "10", + "manufacturer": "FIBARO System", + "product": "FGMS001-ZW5 Motion Sensor", + "neighbors": [1] + } + ], + "get /api/v1/service/usb/port": [ + { + "comName": "/dev/tty.usbmodem145301", + "serialNumber": "f75ab720-bbb3-4a1c-8729-84aa02ebdca0", + "locationId": "14530000", + "vendorId": "0658", + "productId": "0200" + } + ], + "get /api/v1/area": [ + { + "id": "20b4f1f0-989b-4a94-b0d4-c042137da6b5", + "name": "My house", + "radius": 1000, + "color": "#3498db", + "latitude": 41.89154462447053, + "longitude": 12.49828345229836 + }, + { + "id": "f7312c0d-2eac-4e89-9c78-0428e06a39f4", + "name": "My work", + "radius": 2000, + "color": "#f1c40f", + "latitude": 41.93425385676557, + "longitude": 12.402756238310928 + } + ], + "get /api/v1/house": [ + { + "id": "23c40ffe-e1b5-4130-b8df-c56ff92ceee5", + "name": "My House", + "selector": "my-house", + "latitude": 41.89154462447053, + "longitude": 12.49828345229836, + "rooms": [ + { + "id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Living Room", + "selector": "living-room" + }, + { + "id": "f99ab22a-e6a8-4756-b1fe-4d19dc8c8620", + "name": "Kitchen", + "selector": "kitchen" + }, + { + "id": "01ad196a-020d-4828-a7b6-41bde8496823", + "name": "Garden", + "selector": "garden" + } + ] + } + ], + "get /api/v1/service/zwave/device": [ + { + "id": "fbedb47f-4d25-4381-8923-2633b23192a0", + "service_id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Fibaro Motion Sensor", + "selector": "zwave:1234", + "external_id": "test-sensor-external", + "should_poll": false, + "poll_frequency": null, + "created_at": "2019-02-12T07:49:07.556Z", + "updated_at": "2019-02-12T07:49:07.556Z", + "features": [ + { + "name": "Temperature", + "selector": "test-temperature", + "category": "temperature-sensor", + "type": "decimal" + }, + { + "name": "Motion", + "selector": "test-motion", + "category": "motion-sensor", + "type": "binary" + }, + { + "name": "Battery", + "selector": "test-battery", + "category": "battery", + "type": "integer", + "last_value": "92" + }, + { + "name": "Lux", + "selector": "test-light", + "category": "light-sensor", + "type": "integer" + } + ], + "room": { + "id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Living Room", + "selector": "living-room" + } + } + ], + "get /api/v1/service/mqtt": {}, + "get /api/v1/service/mqtt/status": { + "configured": true, + "connected": true + }, + "get /api/v1/service/mqtt/config": { + "useEmbeddedBroker": true, + "dockerBased": true, + "networkModeValid": true, + "brokerContainerAvailable": false + }, + "get /api/v1/service/zigbee2mqtt": {}, + "get /api/v1/service/zigbee2mqtt/permit_join": true, + "get /api/v1/service/zigbee2mqtt/device": [ + { + "name": "Aqara Sensor", + "external_id": "zigbee2mqtt:0x00158d0005828ece", + "selector": "zigbee2mqtt-0x00158d0005828ece", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "model": "WSDCGQ11LM", + "params": [ + { + "name": "model", + "value": "WSDCGQ11LM" + } + ], + "features": [ + { + "category": "pressure-sensor", + "external_id": "zigbee2mqtt:0x00158d0005828ece:pressure-sensor:decimal:pressure", + "name": "Pressure Sensor", + "read_only": true, + "selector": "zigbee2mqtt:0x00158d0005828ece:pressure-sensor:decimal:pressure", + "type": "decimal" + } + ] + } + ], + "get /api/v1/service/zigbee2mqtt/discovered": [ + { + "name": "Aqara Sensor", + "external_id": "zigbee2mqtt:0x00158d0005828ece", + "selector": "zigbee2mqtt-0x00158d0005828ece", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "model": "WSDCGQ11LM", + "supported": true, + "created_at": "2019-02-12T07:49:07.556Z", + "params": [ + { + "name": "model", + "value": "WSDCGQ11LM" + } + ], + "features": [ + { + "category": "pressure-sensor", + "external_id": "zigbee2mqtt:0x00158d0005828ece:pressure-sensor:decimal:pressure", + "name": "Pressure Sensor", + "read_only": true, + "selector": "zigbee2mqtt:0x00158d0005828ece:pressure-sensor:decimal:pressure", + "type": "decimal" + } + ] + }, + { + "model": "WXKG01LM", + "name": "0x00158d00033e88d5", + "service_id": "f87b7af2-ca8e-44fc-b754-444354b42fee", + "should_poll": false, + "supported": true, + "external_id": "zigbee2mqtt:0x00158d00033e88d5", + "features": [ + { + "category": "battery", + "external_id": "zigbee2mqtt:0x00158d00033e88d5:battery:integer:battery", + "has_feedback": false, + "max": 100, + "min": 0, + "name": "Battery", + "read_only": true, + "selector": "zigbee2mqtt-0x00158d00033e88d5-battery-integer-battery", + "type": "integer", + "unit": "percent" + }, + { + "category": "button", + "external_id": "zigbee2mqtt:0x00158d00033e88d5:button:click:action", + "has_feedback": false, + "max": 7, + "min": 0, + "name": "Action", + "read_only": true, + "selector": "zigbee2mqtt-0x00158d00033e88d5-button-click-action", + "type": "click", + "unit": null + }, + { + "category": "switch", + "external_id": "zigbee2mqtt:0x00158d00033e88d5:switch:voltage:voltage", + "has_feedback": false, + "max": 10000, + "min": 0, + "name": "Voltage", + "read_only": true, + "selector": "zigbee2mqtt-0x00158d00033e88d5-switch-voltage-voltage", + "type": "voltage", + "unit": "millivolt" + } + ] + }, + { + "name": "Unsupported device", + "external_id": "zigbee2mqtt:0x00158d0005828ece", + "selector": "zigbee2mqtt-0x00158d0005828ece", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "supported": false, + "features": [ + { + "category": "battery", + "name": "Pressure Sensor", + "read_only": true, + "type": "decimal" + } + ] + } + ], + "get /api/v1/service/zigbee2mqtt/variable/ZIGBEE2MQTT_DRIVER_PATH": {}, + "get /api/v1/service/zigbee2mqtt/status": { + "usbConfigured": true, + "mqttExist": true, + "mqttRunning": true, + "zigbee2mqttExist": true, + "zigbee2mqttRunning": true, + "gladysConnected": true, + "zigbee2mqttConnected": true, + "z2mEnabled": true, + "dockerBased": true, + "networkModeValid": true + }, + "get /api/v1/service/tasmota": {}, + "get /api/v1/service/tasmota/device": [ + { + "name": "Switch", + "external_id": "tasmota:sonoff-basic", + "selector": "tasmota-sonoff-basic", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "model": "sonoff-basic", + "features": [ + { + "category": "switch", + "type": "binary" + } + ] + }, + { + "name": "Switch", + "external_id": "tasmota:192.168.1.1", + "selector": "tasmota-192-168-1-1", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443e", + "features": [ + { + "category": "switch", + "type": "binary" + } + ], + "params": [ + { + "name": "protocol", + "value": "http" + } + ] + } + ], + "get /api/v1/device/tasmota-sonoff-basic": { + "name": "Switch", + "external_id": "tasmota:sonoff-basic", + "selector": "sonoff-basic", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "model": "sonoff-basic", + "features": [ + { + "category": "switch", + "type": "binary", + "name": "Switch" + } + ] + }, + "get /api/v1/device/tasmota-192-168-1-1": { + "name": "Switch", + "external_id": "tasmota:sonoff-basic", + "selector": "sonoff-basic", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "model": "sonoff-basic", + "features": [ + { + "category": "switch", + "type": "binary", + "name": "Switch" + } + ] + }, + "get /api/v1/service/tasmota/discover/mqtt": [ + { + "name": "Sonoff Basic Kitchen", + "external_id": "tasmota:sonoff-basic", + "created_at": "2019-02-12T07:49:07.556Z", + "model": "sonoff-basic", + "features": [ + { + "category": "switch", + "type": "binary" + } + ] + }, + { + "name": "Sonoff Pow Kitchen", + "external_id": "tasmota:sonoff-pow", + "model": "sonoff-pow", + "features": [ + { + "category": "switch", + "type": "binary" + } + ] + }, + { + "name": "Sonoff Mini Outside", + "external_id": "tasmota:sonoff-mini", + "model": "sonoff-basic", + "created_at": "2019-02-12T07:49:07.556Z", + "updatable": true, + "features": [ + { + "category": "switch", + "type": "binary" + } + ] + } + ], + "get /api/v1/service/tasmota/discover/http": [ + { + "name": "Sonoff Basic Kitchen", + "external_id": "tasmota:192.168.1.1", + "created_at": "2019-02-12T07:49:07.556Z", + "model": "sonoff-basic", + "features": [ + { + "category": "switch", + "type": "binary" + } + ], + "params": [ + { + "name": "protocol", + "value": "http" + } + ] + }, + { + "name": "Sonoff Pow Kitchen", + "external_id": "tasmota:192.168.1.2", + "model": "sonoff-pow", + "features": [ + { + "category": "switch", + "type": "binary" + } + ], + "params": [ + { + "name": "protocol", + "value": "http" + } + ] + }, + { + "name": "Sonoff Mini Outside", + "external_id": "tasmota:192.168.1.3", + "model": "sonoff-basic", + "created_at": "2019-02-12T07:49:07.556Z", + "updatable": true, + "features": [ + { + "category": "switch", + "type": "binary" + } + ], + "params": [ + { + "name": "protocol", + "value": "http" + } + ] + }, + { + "name": "192.168.1.3", + "external_id": "tasmota:192.168.1.3", + "created_at": "2019-02-12T07:49:07.556Z", + "needAuthentication": true, + "features": [ + { + "category": "switch", + "type": "binary" + } + ], + "params": [ + { + "name": "protocol", + "value": "http" + } + ] + } + ], + "get /api/v1/service/rtsp-camera/device": [ + { + "id": "c2204fdc-c22f-4fc9-b7d7-c862f3c514c7", + "name": "Kitchen Camera", + "params": [ + { + "name": "CAMERA_URL", + "value": "http://camera-url" + } + ] + } + ], + "get /api/v1/service/rtsp-camera": { + "id": "aa7d6284-6b80-4e78-9e08-a4122207edcd" + }, + "post /api/v1/service/rtsp-camera/camera/test": "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA1AAD/4QMJaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzEzOCA3OS4xNTk4MjQsIDIwMTYvMDkvMTQtMDE6MDk6MDEgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjRFMTgwODcwNzU0MjExRTk5Nzc5RkZBMTY3OTgyRDBEIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjRFMTgwODZGNzU0MjExRTk5Nzc5RkZBMTY3OTgyRDBEIiB4bXA6Q3JlYXRvclRvb2w9IlZlci4xLjAuMDAwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9IkYzN0JCRDNCMzkwRTdEQ0NEMkQ5ODI4MDFGNTkwMTZBIiBzdFJlZjpkb2N1bWVudElEPSJGMzdCQkQzQjM5MEU3RENDRDJEOTgyODAxRjU5MDE2QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/tAEhQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAADxwBWgADGyVHHAIAAAIAAgA4QklNBCUAAAAAABD84R+JyLfJeC80YjQHWHfr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mUgIL2HDcptx537RXyPsbduJzXs011UM7MizJx6zmRtwWN4zqF5buI48a4bdu/nP+WppFFHhxAViViAfPc7XbcOFtNOym2l7JnwS+qGaZnjSI3VV1UjVAdTt8vPgKa6zz8nlVy+s5674k8qtY70J2gXsWsON++t9f19PKXaoMSdJXsyqrEbEY6gNb4mGpPZXXfXEZlNfp+fPneiVLynU3IO0ct2psK69W0s4Tb9XS9O6ZFgxWB3ysPPIefcOwV2kc7VlmABJ0A4mqyz8jMeV/Sgub8TUtawmxen7fNJq1JEtPy8jHxEu583JRxrSMPKz5MhuxeQFFw9ARNK9jzJVSqJAlA4LQHbVB20UttAdtAttAttQLbQHbQLbQLbRR20C20B20C20B21AttAdtAttAdtAttFHbRB20UttAdtAttAttAdtELbQHbUC20C20C20Acqi7mNgOdZ22kma1JmuK+o3xcrPMfpWaJQLptUE637eBr43b2bbbZ8PbprJMObyMibFbZFLtYteQjmp107q1JN/MTOFoT408AlVHaS/AG9ral728puNBXHaevDXnkmzMNg2R+4Utdo18ouQNfLY+81j038cLMeVKdlzGX5dP2pHALPzYDdtL2sLc666/s83lm8pB9N5hlURMhhfWQ6+Q+77q30d38nH4Z2mG/h4EGFEEj1NhvdviYjmf4V7JMOVuT5po4l3ObD761lGcWyM59qDbCDxqZXDRxcKKBL2AtqWNWapazeqfUUMF4cTzyc35DwqkjAfJlncvIxZjzNFPU1B6kiV7XmSqlEPC1Q4LRR20C20B21QttAttAdtQLbQLbQLbUB20UdtAttAdtAttAttAdtAttAdtQHbQLbVUdtQLZRB20B20C20UttEHZQLZQHbRS21ELbVC21BjfUz5sUCNj3EZO1yNtrntD8fZXl+xrvcert1WfLleodVeMOzYyDITdGZVPmsLhdOAFr614tr41+Y9Enz8OZRhkTmSclkj1Y3F72v7tK1tPWcJOUCZTpklVnMSX+NQe46DTUV0mks5T2pkmS4ILMxTduZQdu7ne9udT0/BltdMxuo5sscin08Jk2yk/mPD4f1W51x16Zc/wBWrvY6SOJIo1jTREFgCb/aa9OusnhytyrZubFji3xSclH41rKYVIcPIzJBLk3CHgndSTK24XcnJwemwb5mCgDy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", + "get /api/v1/gateway/status": { + "configured": false, + "connected": false + }, + "get /api/v1/gateway/backup": [ + { + "id": "99fac564-0844-44f6-866b-2b8dcf0d76eb", + "path": "https://url-backup", + "size": 1127680, + "created_at": "2019-06-25T07:01:24.846Z", + "updated_at": "2019-06-25T07:01:24.846Z", + "is_deleted": false + }, + { + "id": "210460b2-c9a8-4891-9cca-464c3e19bfbb", + "path": "https://url-backup", + "size": 1013680, + "created_at": "2019-06-24T03:45:10.144Z", + "updated_at": "2019-06-24T03:45:10.144Z", + "is_deleted": false + } + ], + "get /api/v1/ping": {}, + "get /api/v1/system/info": { + "hostname": "Raspberry Pi 4", + "type": "Linux", + "platform": "linux", + "arch": "x64", + "release": "18.5.0", + "uptime": 662555, + "loadavg": [1.908203125, 3.01513671875, 3.64013671875], + "totalmem": 17179869184, + "freemem": 492482560, + "cpus": [ + { + "model": "Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz", + "speed": 3300, + "times": { + "user": 34606730, + "nice": 0, + "sys": 24855850, + "idle": 100527470, + "irq": 0 + } + }, + { + "model": "Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz", + "speed": 3300, + "times": { + "user": 22568450, + "nice": 0, + "sys": 10605290, + "idle": 126800520, + "irq": 0 + } + }, + { + "model": "Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz", + "speed": 3300, + "times": { + "user": 34765800, + "nice": 0, + "sys": 20890230, + "idle": 104318270, + "irq": 0 + } + }, + { + "model": "Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz", + "speed": 3300, + "times": { + "user": 18691910, + "nice": 0, + "sys": 8683980, + "idle": 132598350, + "irq": 0 + } + } + ], + "network_interfaces": {}, + "nodejs_version": "v10.15.2", + "gladys_version": "v4.0.0", + "is_docker": false, + "new_release_available": false + }, + "get /api/v1/system/disk": { + "filesystem": "/dev/disk1s1", + "size": 499313172480, + "used": 464613756928, + "available": 28587036672, + "capacity": 0.953000005, + "mountpoint": "/" + }, + "get /api/v1/system/container": [ + { + "name": "/gladys", + "state": "running", + "id": "9e5f09775f897624deb1eb2ec8688c1b300d81bc3727fc71ae3290d3d8f71fa9", + "created_at": 1561506899 + } + ], + "get /api/v1/service": [ + { + "id": "27c96cfe-98ce-437b-a83f-5b13e0605203", + "pod_id": null, + "name": "example", + "selector": "example", + "version": "0.1.0", + "has_message_feature": false, + "status": "LOADING", + "created_at": "2020-04-11T18:41:40.051Z", + "updated_at": "2020-10-18T10:13:22.365Z" + }, + { + "id": "40262062-2e71-412c-8da0-70bd03f03b90", + "pod_id": null, + "name": "philips-hue", + "selector": "philips-hue", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.052Z", + "updated_at": "2020-10-30T07:44:07.731Z" + }, + { + "id": "4cd73c14-a929-4af0-a5e2-baed35802224", + "pod_id": null, + "name": "rtsp-camera", + "selector": "rtsp-camera", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.053Z", + "updated_at": "2020-10-30T07:44:07.694Z" + }, + { + "id": "0c27de72-ced7-4f7f-8950-473b9e904e71", + "pod_id": null, + "name": "telegram", + "selector": "telegram", + "version": "0.1.0", + "has_message_feature": true, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.053Z", + "updated_at": "2020-10-30T07:44:07.518Z" + }, + { + "id": "09a3e250-940a-4f52-8595-e6268ffd7198", + "pod_id": null, + "name": "usb", + "selector": "usb", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.053Z", + "updated_at": "2020-10-30T07:44:07.660Z" + }, + { + "id": "366fd9d7-bfbf-4c13-bd8c-4cc777799142", + "pod_id": null, + "name": "xiaomi", + "selector": "xiaomi", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.055Z", + "updated_at": "2020-10-30T07:44:07.474Z" + }, + { + "id": "3772bbf5-b1d7-441f-9bd4-ef94920e31cd", + "pod_id": null, + "name": "zwave", + "selector": "zwave", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.056Z", + "updated_at": "2020-10-30T07:44:07.594Z" + }, + { + "id": "7355bc7f-4109-40ba-819f-fb03f91969b0", + "pod_id": null, + "name": "tasmota", + "selector": "tasmota", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-11T18:41:40.056Z", + "updated_at": "2020-10-30T07:44:07.627Z" + }, + { + "id": "2e0bc58b-11e2-4176-8ad3-9ebc8cdd2318", + "pod_id": null, + "name": "mqtt", + "selector": "mqtt", + "version": "0.1.0", + "has_message_feature": false, + "status": "ERROR", + "created_at": "2020-04-11T18:41:40.057Z", + "updated_at": "2020-10-30T07:44:07.785Z" + }, + { + "id": "d97ba3fa-872f-4ecc-879f-46c55a2930c6", + "pod_id": null, + "name": "google-actions", + "selector": "google-actions", + "version": "0.1.0", + "has_message_feature": false, + "status": "UNKNOWN", + "created_at": "2020-04-11T18:41:40.111Z", + "updated_at": "2020-04-11T18:41:40.111Z" + }, + { + "id": "6d3c7a63-e4b8-4650-bcd3-50cf42b2996f", + "pod_id": null, + "name": "caldav", + "selector": "caldav", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-04-16T19:38:21.885Z", + "updated_at": "2020-10-30T07:44:07.558Z" + }, + { + "id": "39a278e9-66da-47cb-bdaa-264ba6418091", + "pod_id": null, + "name": "openweather", + "selector": "openweather", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-08-19T13:04:57.309Z", + "updated_at": "2020-10-30T07:44:07.814Z" + }, + { + "id": "9682e167-e07f-4823-bd31-a60f957842e0", + "pod_id": null, + "name": "broadlink", + "selector": "broadlink", + "version": "0.1.0", + "has_message_feature": false, + "status": "UNKNOWN", + "created_at": "2020-08-30T15:55:19.467Z", + "updated_at": "2020-08-30T15:55:19.467Z" + }, + { + "id": "d6ea610f-1e33-4c08-89a3-1c8be2cc45f9", + "pod_id": null, + "name": "bluetooth", + "selector": "bluetooth", + "version": "0.1.0", + "has_message_feature": false, + "status": "LOADING", + "created_at": "2020-09-02T12:35:32.763Z", + "updated_at": "2020-10-18T09:28:14.935Z" + }, + { + "id": "c9fe2705-35dc-417b-b6fc-c4bbb9c69886", + "pod_id": null, + "name": "tp-link", + "selector": "tp-link", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-11-11T18:41:40.052Z", + "updated_at": "2020-11-28T07:44:07.731Z" + } + ], + "get /api/v1/session": [ + { + "id": "4b249694-661b-4c48-afb5-924bbedcee63", + "token_type": "refresh_token", + "scope": ["dashboard:write", "dashboard:read"], + "valid_until": "2019-07-26T01:00:50.137Z", + "last_seen": null, + "revoked": false, + "useragent": "Mozilla/5.0 (Linux; Android 6.0.1; SHIELD Tablet K1 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Safari/537.36", + "created_at": "2019-06-26T01:00:50.138Z", + "updated_at": "2019-06-26T01:00:50.138Z" + }, + { + "id": "2367a8cf-47a8-4db7-83b0-f89c2c6c34ac", + "token_type": "refresh_token", + "scope": ["dashboard:write", "dashboard:read"], + "valid_until": "2019-07-26T00:29:00.783Z", + "last_seen": null, + "revoked": false, + "useragent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1", + "created_at": "2019-06-26T00:29:00.783Z", + "updated_at": "2019-06-26T00:29:00.783Z" + }, + { + "id": "2367a8cf-47a8-4db7-83b0-f89c2c6c34ac", + "token_type": "refresh_token", + "scope": ["dashboard:write", "dashboard:read"], + "valid_until": "2019-07-26T00:29:00.783Z", + "last_seen": null, + "revoked": false, + "useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246", + "created_at": "2019-06-26T00:29:00.783Z", + "updated_at": "2019-06-26T00:29:00.783Z" + }, + { + "id": "2367a8cf-47a8-4db7-83b0-f89c2c6c34ac", + "token_type": "refresh_token", + "scope": ["dashboard:write", "dashboard:read"], + "valid_until": "2019-07-26T00:29:00.783Z", + "last_seen": null, + "revoked": false, + "created_at": "2019-06-26T00:29:00.783Z", + "updated_at": "2019-06-26T00:29:00.783Z" + } + ], + "get /api/v1/setup": { + "account_configured": true + }, + "get /api/v1/service/xiaomi/sensor": [ + { + "name": "Xiaomi Temperature", + "external_id": "xiaomi:1234", + "selector": "xiaomi:1234", + "features": [ + { + "name": "Temperature", + "selector": "xiaomi:12344:temperature", + "external_id": "xiaomi:12344:temperature", + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": -20, + "max": 60 + }, + { + "name": "Humidity", + "selector": "xiaomi:12344:humidity", + "external_id": "xiaomi:12344:humidity", + "category": "humidity-sensor", + "type": "decimal", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + }, + { + "name": "Battery", + "selector": "xiaomi:12344:battery", + "external_id": "xiaomi:12344:battery", + "category": "battery", + "type": "integer", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + } + ] + } + ], + "get /api/v1/service/xiaomi/device": [ + { + "id": "e5317b24-28e1-4839-9879-0bb7a3102e98", + "name": "Xiaomi Temperature", + "external_id": "xiaomi:1234", + "selector": "xiaomi:1234", + "room_id": "f99ab22a-e6a8-4756-b1fe-4d19dc8c8620", + "service_id": "70cb1e17-3b17-4886-83ab-45b00a9e03b1", + "features": [ + { + "name": "Temperature", + "selector": "xiaomi:12344:temperature", + "external_id": "xiaomi:12344:temperature", + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": -20, + "max": 60 + }, + { + "name": "Humidity", + "selector": "xiaomi:12344:humidity", + "external_id": "xiaomi:12344:humidity", + "category": "humidity-sensor", + "type": "decimal", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + }, + { + "name": "Battery", + "selector": "xiaomi:12344:battery", + "external_id": "xiaomi:12344:battery", + "category": "battery", + "type": "integer", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + } + ] + } + ], + "get /api/v1/device": [ + { + "id": "06e735a3-ac62-4a05-85b6-855f2c556d7b", + "name": "Living room lamp", + "selector": "light", + "features": [ + { + "name": "Living room lamp", + "type": "binary", + "selector": "light.binary", + "category": "light" + } + ] + } + ], + "get /api/v1/service/xiaomi": { + "id": "70cb1e17-3b17-4886-83ab-45b00a9e03b1", + "name": "Xiaomi", + "selector": "xiaomi" + }, + "get /api/v1/device/zwave:1234": { + "id": "fbedb47f-4d25-4381-8923-2633b23192a0", + "service_id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Fibaro Motion Sensor", + "selector": "zwave:1234", + "external_id": "test-sensor-external", + "should_poll": false, + "poll_frequency": null, + "created_at": "2019-02-12T07:49:07.556Z", + "updated_at": "2019-02-12T07:49:07.556Z", + "features": [ + { + "name": "Temperature", + "external_id": "zwave:1234:temperature", + "selector": "test-temperature", + "category": "temperature-sensor", + "unit": "celsius", + "type": "decimal" + }, + { + "name": "Motion", + "selector": "test-motion", + "external_id": "zwave:1234:temperature", + "category": "motion-sensor", + "type": "binary" + }, + { + "name": "Battery", + "selector": "test-battery", + "external_id": "zwave:1234:temperature", + "category": "battery", + "type": "integer", + "last_value": "92" + }, + { + "name": "Lux", + "selector": "test-light", + "external_id": "zwave:1234:temperature", + "category": "light-sensor", + "type": "integer" + } + ], + "room": { + "id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Living Room", + "selector": "living-room" + } + }, + "get /api/v1/service/zwave": { + "id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "name": "Zwave", + "selector": "zwave" + }, + "get /api/v1/device/xiaomi:1234": { + "id": "e5317b24-28e1-4839-9879-0bb7a3102e98", + "name": "Xiaomi Temperature", + "external_id": "xiaomi:1234", + "selector": "xiaomi:1234", + "room_id": "f99ab22a-e6a8-4756-b1fe-4d19dc8c8620", + "service_id": "70cb1e17-3b17-4886-83ab-45b00a9e03b1", + "features": [ + { + "name": "Temperature", + "selector": "xiaomi:12344:temperature", + "external_id": "xiaomi:12344:temperature", + "category": "temperature-sensor", + "type": "decimal", + "unit": "celsius", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": -20, + "max": 60 + }, + { + "name": "Humidity", + "selector": "xiaomi:12344:humidity", + "external_id": "xiaomi:12344:humidity", + "category": "humidity-sensor", + "type": "decimal", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + }, + { + "name": "Battery", + "selector": "xiaomi:12344:battery", + "external_id": "xiaomi:12344:battery", + "category": "battery", + "type": "integer", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + } + ] + }, + "get /api/v1/service/philips-hue": { + "id": "1147bdef-0c95-40f1-a7ef-922ebcad7d0e", + "name": "Philips Hue", + "selector": "philips-hue" + }, + "get /api/v1/service/philips-hue/light": [ + { + "id": "1", + "name": "New Lamp", + "model": "LCT007", + "external_id": "philips-hue:4" + }, + { + "id": "2", + "name": "Living room lamp", + "model": "LCT007", + "external_id": "philips-hue:5" + } + ], + "get /api/v1/service/philips-hue/device": [ + { + "id": "1", + "name": "Lounge Living Color", + "model": "LCT007", + "external_id": "philips-hue:1", + "features": [ + { + "name": "On/Off", + "category": "light", + "type": "binary", + "min": 0, + "max": 1 + }, + { + "name": "Color", + "category": "light", + "type": "color", + "min": 0, + "max": 1 + } + ] + }, + { + "id": "2", + "name": "Right Bedside", + "type": "Extended color light", + "model": "LCT001", + "external_id": "philips-hue:2", + "features": [ + { + "name": "On/Off", + "category": "light", + "type": "binary", + "min": 0, + "max": 1 + }, + { + "name": "Color", + "category": "light", + "type": "color", + "min": 0, + "max": 1 + } + ] + }, + { + "id": "3", + "name": "Left Bedside", + "type": "Extended color light", + "model": "LCT001", + "external_id": "philips-hue:3", + "features": [ + { + "name": "On/Off", + "category": "light", + "type": "binary", + "min": 0, + "max": 1 + }, + { + "name": "Color", + "category": "light", + "type": "color", + "min": 0, + "max": 1 + } + ] + } + ], + "get /api/v1/service/bluetooth": { + "id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "name": "bluetooth", + "enabled": true + }, + "get /api/v1/service/bluetooth/config": { + "presenceScanner": { + "status": "enabled", + "frequency": 60000 + } + }, + "get /api/v1/service/bluetooth/device": [ + { + "id": "fbedb47f-4d25-4381-8923-2633b23192a0", + "service_id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Nut Smart Tracker", + "selector": "bluetooth-sensor", + "external_id": "test-sensor-external", + "should_poll": false, + "poll_frequency": null, + "created_at": "2019-02-12T07:49:07.556Z", + "updated_at": "2019-02-12T07:49:07.556Z", + "features": [ + { + "name": "Battery", + "selector": "test-battery", + "category": "battery", + "type": "integer", + "last_value": "12" + } + ], + "room": { + "id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Living Room", + "selector": "living-room" + } + } + ], + "get /api/v1/device/bluetooth-sensor": { + "id": "fbedb47f-4d25-4381-8923-2633b23192a0", + "service_id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "room_id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Nut Smart Tracker", + "selector": "bluetooth-sensor", + "external_id": "bluetooth:external", + "should_poll": false, + "poll_frequency": null, + "created_at": "2019-02-12T07:49:07.556Z", + "updated_at": "2019-02-12T07:49:07.556Z", + "features": [ + { + "name": "Battery", + "selector": "test-battery", + "category": "battery", + "type": "integer", + "last_value": "12" + } + ], + "room": { + "id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Living Room", + "selector": "living-room" + } + }, + "get /api/v1/service/bluetooth/status": { + "ready": true + }, + "get /api/v1/service/bluetooth/peripheral": [ + { + "name": "BLE Device 1", + "external_id": "bluetooth:0011223341", + "service_id": "a810b8db-6d04-4697-bed3-c4b72c996279", + "selector": "bluetooth-0011223341", + "params": [ + { + "name": "loaded", + "value": false + } + ] + }, + { + "name": "SML c9", + "model": "smlc9", + "external_id": "bluetooth:0011223342", + "selector": "bluetooth-0011223342", + "params": [ + { + "name": "loaded", + "value": true + }, + { + "name": "manufacturer", + "value": "AwoX" + } + ] + }, + { + "name": "Peanut temperature", + "external_id": "bluetooth:0011223343", + "service_id": "a810b8db-6d04-4697-bed3-c4b72c996278", + "service": { + "name": "peanut" + }, + "selector": "bluetooth-0011223343", + "params": [ + { + "name": "loaded", + "value": true + }, + { + "name": "manufacturer", + "value": "Peanut" + } + ], + "features": [ + { + "name": "Battery", + "category": "battery", + "type": "integer", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + }, + { + "name": "Temperature", + "category": "temperature-sensor", + "type": "decimal", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": -100, + "max": 250 + } + ] + } + ], + "get /api/v1/service/bluetooth/peripheral/bluetooth-0011223341": { + "name": "BLE Device 1", + "external_id": "bluetooth:0011223341", + "selector": "bluetooth-0011223341", + "params": [ + { + "name": "loaded", + "value": false + } + ] + }, + "get /api/v1/service/bluetooth/peripheral/bluetooth-0011223342": { + "name": "SML c9", + "model": "smlc9", + "external_id": "bluetooth:0011223342", + "selector": "bluetooth-0011223342", + "params": [ + { + "name": "loaded", + "value": true + }, + { + "name": "manufacturer", + "value": "AwoX" + } + ] + }, + "get /api/v1/service/bluetooth/peripheral/bluetooth-0011223343": { + "name": "Peanut temperature", + "external_id": "bluetooth:0011223343", + "selector": "bluetooth-0011223343", + "params": [ + { + "name": "loaded", + "value": true + }, + { + "name": "manufacturer", + "value": "Peanut" + } + ], + "features": [ + { + "name": "Battery", + "category": "battery", + "type": "integer", + "unit": "percent", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": 0, + "max": 100 + }, + { + "name": "Temperature", + "category": "temperature-sensor", + "type": "decimal", + "read_only": true, + "keep_history": true, + "has_feedback": true, + "min": -100, + "max": 250 + } + ] + }, + "get /api/v1/service/ewelink": { + "id": "45c792a5-051b-4e6f-b746-2dd4c77d9d31", + "name": "ewelink", + "selector": "ewelink" + }, + "get api/v1/service/ewelink/device": [ + { + "id": "28e8ad03-70a8-431f-93cb-df916019c509", + "room_id": "568981d0-1a4d-40ea-af97-dd4037d2b344", + "name": "Switch 1", + "selector": "ewelink-1000768322-0", + "model": "MINI", + "external_id": "ewelink:1000768322:0", + "should_poll": true, + "poll_frequency": 60000, + "features": [ + { + "id": "6f8172ed-37e5-4785-94ad-ec33706a31f3", + "device_id": "28e8ad03-70a8-431f-93cb-df916019c509", + "name": "Switch 1 On/Off", + "selector": "ewelink-1000768322-0-binary", + "external_id": "ewelink:1000768322:0:binary", + "category": "switch", + "type": "binary", + "read_only": false, + "has_feedback": false, + "min": 0, + "max": 1 + } + ], + "params": [ + { + "id": "5e1ef948-305b-44c5-bb78-78952b1f5cb2", + "device_id": "28e8ad03-70a8-431f-93cb-df916019c509", + "name": "IP_ADDRESS", + "value": "0.0.0.1" + }, + { + "id": "f3a6f3fa-a7b0-4968-b9fd-2e492ced2274", + "device_id": "28e8ad03-70a8-431f-93cb-df916019c509", + "name": "FIRMWARE", + "value": "3.3.0" + } + ], + "room": { + "id": "cecc52c7-3e67-4b75-9b13-9a8867b0443d", + "name": "Living Room", + "selector": "living-room" + }, + "service": { + "id": "45c792a5-051b-4e6f-b746-2dd4c77d9d31", + "name": "ewelink", + "selector": "ewelink" + } + } + ], + "get api/v1/service/ewelink/discover": [ + { + "service_id": "45c792a5-051b-4e6f-b746-2dd4c77d9d31", + "name": "Switch 2", + "model": "Basic", + "external_id": "ewelink:10004636bf:0", + "selector": "ewelink:10004636bf:0", + "should_poll": true, + "poll_frequency": 60000, + "features": [ + { + "name": "Switch 2 On/Off", + "external_id": "ewelink:10004636bf:0:binary", + "selector": "ewelink:10004636bf:0:binary", + "category": "switch", + "type": "binary", + "read_only": false, + "has_feedback": false, + "min": 0, + "max": 1 + } + ], + "params": [ + { + "name": "IP_ADDRESS", + "value": "0.0.0.2" + }, + { + "name": "FIRMWARE", + "value": "3.2.1" + } + ] + } + ], + "get /api/v1/service/tp-link": { + "id": "c9fe2705-35dc-417b-b6fc-c4bbb9c69886", + "pod_id": null, + "name": "tp-link", + "selector": "tp-link", + "version": "0.1.0", + "has_message_feature": false, + "status": "RUNNING", + "created_at": "2020-11-11T18:41:40.052Z", + "updated_at": "2020-11-28T07:44:07.731Z" + }, + "get /api/v1/service/tp-link/device": [ + { + "id": "1", + "name": "Plug Coffee Machine", + "model": "HS100", + "external_id": "tp-link-1", + "features": [ + { + "name": "On/Off", + "category": "switch", + "type": "binary", + "min": 0, + "max": 1 + } + ] + }, + { + "id": "2", + "name": "Light Swimming Pool", + "model": "LB100", + "external_id": "tp-link-2", + "features": [ + { + "name": "On/Off", + "category": "light", + "type": "binary", + "min": 0, + "max": 1 + } + ] + } + ], + "get /api/v1/service/tp-link/scan": [ + { + "id": "3", + "name": "Plug TV Dock", + "model": "HS100", + "external_id": "tp-link-3", + "features": [ + { + "name": "On/Off", + "category": "switch", + "type": "binary", + "min": 0, + "max": 1 + } + ] + }, + { + "id": "4", + "name": "Light Bedroom", + "model": "LB100", + "external_id": "tp-link-4", + "features": [ + { + "name": "On/Off", + "category": "light", + "type": "binary", + "min": 0, + "max": 1 + } + ] + } + ] +} diff --git a/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js b/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js index 79859ca114..37ba70c890 100644 --- a/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js +++ b/front/src/routes/integration/all/zigbee2mqtt/discover-page/actions.js @@ -13,7 +13,7 @@ function createActions(store) { }); try { - const zigbee2mqttDevices = await state.httpClient.get('/api/v1/service/zigbee2mqtt/device'); + const zigbee2mqttDevices = await state.httpClient.get('/api/v1/service/zigbee2mqtt/discovered'); store.setState({ zigbee2mqttDevices, discoverZigbee2mqtt: false }); } catch (e) { store.setState({ diff --git a/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js b/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js index 31a1e85915..c14929a254 100644 --- a/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js +++ b/server/services/zigbee2mqtt/api/zigbee2mqtt.controller.js @@ -3,7 +3,7 @@ const logger = require('../../../utils/logger'); module.exports = function Zigbee2mqttController(gladys, zigbee2mqttManager) { /** - * @api {get} /api/v1/service/zigbee2mqtt/device Get discovered Zigbee2mqtt devices + * @api {get} /api/v1/service/zigbee2mqtt/discovered Get discovered Zigbee2mqtt devices * @apiName getDiscoveredDevices * @apiGroup Zigbee2mqtt */ @@ -101,7 +101,7 @@ module.exports = function Zigbee2mqttController(gladys, zigbee2mqttManager) { } return { - 'get /api/v1/service/zigbee2mqtt/device': { + 'get /api/v1/service/zigbee2mqtt/discovered': { authenticated: true, controller: asyncMiddleware(getDiscoveredDevices), }, diff --git a/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js b/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js index 8397994f81..cb2de44cfa 100644 --- a/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js +++ b/server/test/services/zigbee2mqtt/api/zigbee2mqtt.controller.test.js @@ -29,13 +29,13 @@ describe('zigbee2mqtt API', () => { sinon.reset(); }); - it('get /api/v1/service/zigbee2mqtt/device', async () => { + it('get /api/v1/service/zigbee2mqtt/discovered', async () => { const req = {}; const res = { json: fake.returns(null), }; - await controller['get /api/v1/service/zigbee2mqtt/device'].controller(req, res); + await controller['get /api/v1/service/zigbee2mqtt/discovered'].controller(req, res); assert.calledOnce(zigbee2mqttManager.getDiscoveredDevices); assert.calledWith(res.json, ['device']); From 27ca5d2878148da75ab4eafa2b1154b63034baa2 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Mon, 1 Nov 2021 08:33:59 +0100 Subject: [PATCH 9/9] remove formaldehyd --- server/services/zigbee2mqtt/exposes/numericType.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/services/zigbee2mqtt/exposes/numericType.js b/server/services/zigbee2mqtt/exposes/numericType.js index ce2cd9b9bd..fef9b07957 100644 --- a/server/services/zigbee2mqtt/exposes/numericType.js +++ b/server/services/zigbee2mqtt/exposes/numericType.js @@ -100,12 +100,6 @@ module.exports = { unit: DEVICE_FEATURE_UNITS.KILOWATT_HOUR, }, }, - formaldehyd: { - feature: { - category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR, - type: DEVICE_FEATURE_TYPES.SENSOR.DECIMAL, - }, - }, gas: { feature: { category: DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR,