├── .all-contributorsrc ├── .devcontainer └── devcontainer.json ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md ├── SUPPORT.md ├── stale.yml └── workflows │ ├── build-apidoc-documentation.yml │ ├── build-demo-website.yml │ ├── docker-dev-build.yml │ ├── docker-master-test.yml │ ├── docker-pr-build.yml │ ├── docker-release-build.yml │ ├── publish-gladys-plus-production.yml │ └── relative-ci.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── codecov.yml ├── docker ├── Dockerfile ├── Dockerfile.buildx └── docker-compose.yml ├── front ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── README.md ├── cli │ └── check_translations.js ├── cypress │ ├── cypress.config.js │ ├── e2e │ │ └── routes │ │ │ ├── 0-signup │ │ │ └── SignUp.cy.js │ │ │ ├── dashboard │ │ │ ├── Dashboard.cy.js │ │ │ ├── DashboardHumidityBox.cy.js │ │ │ └── DashboardTemperatureBox.cy.js │ │ │ ├── integration │ │ │ ├── bluetooth │ │ │ │ ├── BluetoothDisabled.cy.js │ │ │ │ ├── devices │ │ │ │ │ ├── BluetoothDeviceEdit.cy.js │ │ │ │ │ └── BluetoothDeviceList.cy.js │ │ │ │ ├── discover │ │ │ │ │ ├── BluetoothDiscover.cy.js │ │ │ │ │ ├── BluetoothDiscovered.cy.js │ │ │ │ │ ├── BluetoothStateChange.cy.js │ │ │ │ │ └── create │ │ │ │ │ │ ├── BluetoothCreateDevice.cy.js │ │ │ │ │ │ └── BluetoothNoDevice.cy.js │ │ │ │ └── scanner │ │ │ │ │ ├── BluetoothScanner.cy.js │ │ │ │ │ └── BluetoothStateChange.cy.js │ │ │ ├── broadlink │ │ │ │ ├── devices │ │ │ │ │ ├── BroadlinkDeviceList.cy.js │ │ │ │ │ ├── BroadlinkEditDevice.cy.js │ │ │ │ │ ├── BroadlinkEditRemote.cy.js │ │ │ │ │ ├── BroadlinkEditRemoteLearnAll.cy.js │ │ │ │ │ └── BroadlinkEditRemoteSkipAll.cy.js │ │ │ │ └── peripherals │ │ │ │ │ └── BroadlinkPeripheralList.cy.js │ │ │ └── zigbee2mqtt │ │ │ │ └── setup │ │ │ │ ├── Zigbee2MqttSetupLocalContainers.cy.js │ │ │ │ └── Zigbee2MqttSetupRemoteGladys.cy.js │ │ │ ├── login │ │ │ └── Login.cy.js │ │ │ ├── maps │ │ │ └── Maps.cy.js │ │ │ └── scene │ │ │ └── Scene.cy.js │ ├── fixtures │ │ └── integration │ │ │ └── routes │ │ │ └── integration │ │ │ ├── bluetooth │ │ │ ├── peripherals.json │ │ │ ├── status_not_ready.json │ │ │ ├── status_ready.json │ │ │ └── status_scanning.json │ │ │ ├── broadlink │ │ │ └── peripherals.json │ │ │ ├── usb │ │ │ └── get_available_usb_ports.json │ │ │ └── zigbee2mqtt │ │ │ ├── status_not_ready_to_setup.json │ │ │ └── status_ready_to_setup.json │ └── support │ │ ├── commands.js │ │ └── e2e.js ├── netlify.toml ├── old-sw.js ├── package-lock.json ├── package.json ├── preact.config.js ├── relativeci.config.js └── src │ ├── actions │ ├── calendar.js │ ├── createScene.js │ ├── dashboard │ │ ├── boxActions.js │ │ ├── boxes │ │ │ ├── humidityInRoom.js │ │ │ ├── temperatureInRoom.js │ │ │ └── weather.js │ │ └── index.js │ ├── device.js │ ├── edit-device.js │ ├── gateway.js │ ├── gatewayLinkUser.js │ ├── house.js │ ├── integration.js │ ├── login │ │ ├── login.js │ │ └── loginGateway.js │ ├── main.js │ ├── map.js │ ├── message.js │ ├── profile.js │ ├── profilePicture.js │ ├── resetPassword.js │ ├── scene.js │ ├── session.js │ ├── signup │ │ ├── signupConfigureHouse.js │ │ ├── signupCreateLocalAccount.js │ │ ├── signupSetPreferences.js │ │ └── welcome.js │ └── system.js │ ├── assets │ ├── favicon.ico │ ├── icons │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192-round.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-512x512-round.png │ │ ├── android-icon-512x512.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ └── icon-sprite.svg │ ├── images │ │ ├── home-icon.png │ │ ├── undraw_credit_card_payments.svg │ │ ├── undraw_personalization.svg │ │ ├── undraw_typing.svg │ │ └── welcome.jpg │ ├── integrations │ │ ├── cover │ │ │ ├── airplay.jpg │ │ │ ├── alexa.jpg │ │ │ ├── bluetooth.jpg │ │ │ ├── broadlink.jpg │ │ │ ├── caldav.jpg │ │ │ ├── callmebot.jpg │ │ │ ├── darksky.jpg │ │ │ ├── enedis.jpg │ │ │ ├── ewelink.jpg │ │ │ ├── free-mobile.jpg │ │ │ ├── google-cast.jpg │ │ │ ├── google-home.jpg │ │ │ ├── homekit.jpg │ │ │ ├── lan-manager.jpg │ │ │ ├── matter.jpg │ │ │ ├── melcloud.jpg │ │ │ ├── mqtt.jpg │ │ │ ├── netatmo.jpg │ │ │ ├── nextcloud-talk.jpg │ │ │ ├── node-red.jpg │ │ │ ├── openai.jpg │ │ │ ├── openweather.jpg │ │ │ ├── owntracks.jpg │ │ │ ├── philips-hue.jpg │ │ │ ├── rtsp-camera.jpg │ │ │ ├── sonos.jpg │ │ │ ├── tasmota.jpg │ │ │ ├── telegram.jpg │ │ │ ├── tp-link.jpg │ │ │ ├── tuya.jpg │ │ │ ├── wemo.jpg │ │ │ ├── xiaomi.jpg │ │ │ ├── zigbee2mqtt.jpg │ │ │ └── zwave-js-ui.jpg │ │ ├── devices │ │ │ └── netatmo │ │ │ │ ├── netatmo-NAMain.jpg │ │ │ │ ├── netatmo-NAModule1.jpg │ │ │ │ ├── netatmo-NAModule2.jpg │ │ │ │ ├── netatmo-NAModule3.jpg │ │ │ │ ├── netatmo-NAModule4.jpg │ │ │ │ ├── netatmo-NAPlug.jpg │ │ │ │ ├── netatmo-NATherm1.jpg │ │ │ │ └── netatmo-NRV.jpg │ │ ├── logos │ │ │ ├── logo_mqtt.png │ │ │ ├── logo_node-red.png │ │ │ └── logo_zigbee2mqtt.png │ │ └── zwavejs-ui │ │ │ ├── zwavejs-ui-gateway-configuration.jpg │ │ │ └── zwavejs-ui-mqtt-configuration.jpg │ ├── leaflet │ │ ├── layers-2x.png │ │ ├── layers.png │ │ ├── marker-icon-2x.png │ │ ├── marker-icon.png │ │ └── marker-shadow.png │ └── splash │ │ ├── apple-splash-1125-2436.jpg │ │ ├── apple-splash-1136-640.jpg │ │ ├── apple-splash-1170-2532.jpg │ │ ├── apple-splash-1242-2208.jpg │ │ ├── apple-splash-1242-2688.jpg │ │ ├── apple-splash-1284-2778.jpg │ │ ├── apple-splash-1334-750.jpg │ │ ├── apple-splash-1536-2048.jpg │ │ ├── apple-splash-1620-2160.jpg │ │ ├── apple-splash-1668-2224.jpg │ │ ├── apple-splash-1668-2388.jpg │ │ ├── apple-splash-1792-828.jpg │ │ ├── apple-splash-2048-1536.jpg │ │ ├── apple-splash-2048-2732.jpg │ │ ├── apple-splash-2160-1620.jpg │ │ ├── apple-splash-2208-1242.jpg │ │ ├── apple-splash-2224-1668.jpg │ │ ├── apple-splash-2388-1668.jpg │ │ ├── apple-splash-2436-1125.jpg │ │ ├── apple-splash-2532-1170.jpg │ │ ├── apple-splash-2688-1242.jpg │ │ ├── apple-splash-2732-2048.jpg │ │ ├── apple-splash-2778-1284.jpg │ │ ├── apple-splash-640-1136.jpg │ │ ├── apple-splash-750-1334.jpg │ │ └── apple-splash-828-1792.jpg │ ├── components │ ├── app.jsx │ ├── boxs │ │ ├── SelectBoxType.jsx │ │ ├── alarm │ │ │ ├── Alarm.jsx │ │ │ ├── Coutdown.jsx │ │ │ ├── EditAlarm.jsx │ │ │ ├── countdown.css │ │ │ └── style.css │ │ ├── baseEditBox.jsx │ │ ├── camera │ │ │ ├── Camera.jsx │ │ │ ├── EditCamera.jsx │ │ │ └── style.css │ │ ├── chart │ │ │ ├── ApexChartAreaOptions.js │ │ │ ├── ApexChartBarOptions.js │ │ │ ├── ApexChartComponent.jsx │ │ │ ├── ApexChartLineOptions.js │ │ │ ├── ApexChartStepLineOptions.js │ │ │ ├── ApexChartTimelineOptions.js │ │ │ ├── Chart.jsx │ │ │ ├── EditChart.jsx │ │ │ ├── style.css │ │ │ └── yAxisFormatter.js │ │ ├── clock │ │ │ ├── Clock.jsx │ │ │ ├── ClockTypes.js │ │ │ ├── EditClock.jsx │ │ │ └── style.css │ │ ├── device-in-room │ │ │ ├── DeviceCard.jsx │ │ │ ├── DeviceRow.jsx │ │ │ ├── DevicesBox.jsx │ │ │ ├── DevicesInRoomsBox.jsx │ │ │ ├── EditDeviceInRoom.jsx │ │ │ ├── EditDevices.jsx │ │ │ ├── SupportedFeatureTypes.jsx │ │ │ ├── device-features │ │ │ │ ├── AirConditioningModeDeviceFeature.jsx │ │ │ │ ├── BinaryDeviceFeature.jsx │ │ │ │ ├── ColorDeviceFeature.jsx │ │ │ │ ├── CoverDeviceFeature.jsx │ │ │ │ ├── LMHVolumeDeviceFeature.jsx │ │ │ │ ├── LightTemperatureDeviceFeature.jsx │ │ │ │ ├── MultiLevelDeviceFeature.jsx │ │ │ │ ├── NumberDeviceFeature.jsx │ │ │ │ ├── PilotWireModeDeviceFeature.jsx │ │ │ │ ├── PushDeviceFeature.jsx │ │ │ │ ├── ThermostatDeviceFeature.jsx │ │ │ │ ├── sensor-value │ │ │ │ │ ├── BadgeNumberDeviceValue.jsx │ │ │ │ │ ├── BinaryDeviceValue.jsx │ │ │ │ │ ├── ButtonClickDeviceValue.jsx │ │ │ │ │ ├── IconBinaryDeviceValue.jsx │ │ │ │ │ ├── LastSeenDeviceValue.jsx │ │ │ │ │ ├── LevelSensorDeviceValue.jsx │ │ │ │ │ ├── MotionSensorDeviceValue.jsx │ │ │ │ │ ├── NoRecentValueBadge.jsx │ │ │ │ │ ├── RawDeviceValue.jsx │ │ │ │ │ ├── SensorDeviceFeature.jsx │ │ │ │ │ ├── SignalQualityDeviceValue.jsx │ │ │ │ │ ├── TemperatureSensorDeviceValue.jsx │ │ │ │ │ └── TextDeviceValue.jsx │ │ │ │ └── style.css │ │ │ └── style.css │ │ ├── ecowatt │ │ │ ├── Ecowatt.jsx │ │ │ └── EditEcowatt.jsx │ │ ├── edf-tempo │ │ │ ├── EdfTempo.jsx │ │ │ ├── EditEdfTempo.jsx │ │ │ └── style.css │ │ ├── gauge │ │ │ ├── EditGaugeBox.jsx │ │ │ └── GaugeBox.jsx │ │ ├── music │ │ │ ├── EditMusicBox.jsx │ │ │ └── MusicBox.jsx │ │ ├── room-humidity │ │ │ ├── EditRoomHumidityBox.jsx │ │ │ └── RoomHumidity.jsx │ │ ├── room-temperature │ │ │ ├── EditRoomTemperatureBox.jsx │ │ │ └── RoomTemperature.jsx │ │ ├── scene │ │ │ ├── EditSceneBox.jsx │ │ │ ├── SceneBox.jsx │ │ │ ├── SceneRow.jsx │ │ │ └── style.css │ │ ├── user-presence │ │ │ ├── EditUserPresenceBox.jsx │ │ │ └── UserPresence.jsx │ │ └── weather │ │ │ ├── EditWeatherBox.jsx │ │ │ └── WeatherBox.jsx │ ├── device │ │ ├── ColorPicker.jsx │ │ ├── RelativeTime.jsx │ │ ├── SelectDeviceFeature.jsx │ │ ├── SelectPilotWireMode.jsx │ │ ├── ShutterButtons.jsx │ │ ├── UpdateDevice.jsx │ │ ├── UpdateDeviceFeature.jsx │ │ ├── UpdateDeviceForm.jsx │ │ ├── index.js │ │ └── view │ │ │ ├── BatteryLevelFeature.jsx │ │ │ ├── DeviceFeature.jsx │ │ │ └── DeviceFeatures.jsx │ ├── documentation │ │ └── DeviceConfigurationLink.jsx │ ├── drag-and-drop │ │ ├── AutoScrollMobile.jsx │ │ ├── DeviceListWithDragAndDrop.jsx │ │ └── style.css │ ├── gateway │ │ ├── GatewayAccountExpired.jsx │ │ ├── GatewayLoginForm.jsx │ │ └── style.css │ ├── header │ │ └── index.jsx │ ├── house │ │ ├── EditHouse.jsx │ │ ├── EditHouseComponent.jsx │ │ ├── EditRoom.jsx │ │ ├── Map.jsx │ │ └── RoomSelector.jsx │ ├── icons │ │ └── SvgIcon.jsx │ ├── layout │ │ ├── CardFilter.jsx │ │ └── index.jsx │ ├── router │ │ ├── Redirect.js │ │ └── ScrollToTopLink.jsx │ ├── scene │ │ └── TextWithVariablesInjected.jsx │ └── user │ │ ├── EditableProfilePicture.jsx │ │ └── profile.jsx │ ├── config.js │ ├── config │ ├── demo.js │ ├── i18n │ │ ├── de.json │ │ ├── en.json │ │ ├── fr.json │ │ └── index.js │ ├── integrations │ │ ├── calendars.json │ │ ├── communications.json │ │ ├── devices.json │ │ ├── index.js │ │ └── weathers.json │ └── timezones.js │ ├── index.js │ ├── manifest.json │ ├── routes │ ├── calendar │ │ └── index.js │ ├── chat │ │ ├── ChatItems.js │ │ ├── ChatPage.js │ │ ├── EmptyChat.jsx │ │ ├── index.js │ │ └── style.css │ ├── dashboard │ │ ├── Box.jsx │ │ ├── BoxColumns.jsx │ │ ├── DashboardPage.jsx │ │ ├── EmptyState.jsx │ │ ├── SetTabletMode.jsx │ │ ├── edit-dashboard │ │ │ ├── BottomDropZone.jsx │ │ │ ├── EditActions.jsx │ │ │ ├── EditBox.jsx │ │ │ ├── EditBoxColumns.jsx │ │ │ ├── EditDashboard.jsx │ │ │ ├── EmptyColumnDropZone.jsx │ │ │ ├── ReorderDashbordList.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ ├── index.js │ │ ├── new-dashboard │ │ │ ├── index.js │ │ │ └── style.css │ │ └── style.css │ ├── error │ │ └── index.jsx │ ├── forgot-password │ │ ├── ForgotPasswordPage.jsx │ │ └── index.js │ ├── gateway-configure-two-factor │ │ ├── ConfigureTwoFactorForm.js │ │ └── index.js │ ├── gateway-confirm-email │ │ ├── ConfirmEmail.js │ │ └── index.js │ ├── gateway-forgot-password │ │ ├── ForgotPassword.js │ │ └── index.js │ ├── gateway-reset-password │ │ ├── ResetPassword.js │ │ ├── ResetPasswordForm.js │ │ └── index.js │ ├── gateway-setup │ │ ├── LinkGatewayUser.jsx │ │ └── index.js │ ├── integration │ │ ├── IntegrationCategory.jsx │ │ ├── IntegrationMenu.jsx │ │ ├── IntegrationPage.jsx │ │ ├── all │ │ │ ├── airplay │ │ │ │ ├── AirplayDeviceBox.jsx │ │ │ │ ├── AirplayPage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ └── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ ├── alexa-gateway │ │ │ │ ├── Layout.jsx │ │ │ │ ├── index.js │ │ │ │ ├── style.css │ │ │ │ └── welcome.jsx │ │ │ ├── bluetooth │ │ │ │ ├── BluetoothPage.js │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── commons │ │ │ │ │ ├── CheckBluetoothPanel.js │ │ │ │ │ └── actions.js │ │ │ │ ├── device-page │ │ │ │ │ ├── BluetoothDevice.jsx │ │ │ │ │ ├── BluetoothDeviceTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ ├── edit-page │ │ │ │ │ └── index.js │ │ │ │ ├── settings-page │ │ │ │ │ ├── BluetoothPresenceScanner.jsx │ │ │ │ │ ├── BluetoothSettingsTab.jsx │ │ │ │ │ └── index.js │ │ │ │ ├── setup-page │ │ │ │ │ ├── BluetoothPeripheral.jsx │ │ │ │ │ ├── BluetoothPeripheralFeatures.jsx │ │ │ │ │ ├── BluetoothPeripheralTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── setup-peripheral │ │ │ │ │ │ ├── ConfigurePeripheral.jsx │ │ │ │ │ │ ├── ConfigurePeripheralForm.jsx │ │ │ │ │ │ ├── ConfigurePeripheralSuccess.jsx │ │ │ │ │ │ ├── PeripheralNotFound.jsx │ │ │ │ │ │ └── index.js │ │ │ │ └── style.css │ │ │ ├── broadlink │ │ │ │ ├── BroadlinkPage.js │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceBox.jsx │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ ├── peripheral-page │ │ │ │ │ ├── Peripheral.jsx │ │ │ │ │ ├── PeripheralTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ ├── remote-page │ │ │ │ │ ├── DeviceNotFound.jsx │ │ │ │ │ ├── RemoteCreation.jsx │ │ │ │ │ ├── RemoteFeatureEditionPanel.jsx │ │ │ │ │ ├── edition │ │ │ │ │ │ ├── RemoteFeatureEdition.jsx │ │ │ │ │ │ └── RemoteFeatureTag.jsx │ │ │ │ │ ├── features.js │ │ │ │ │ └── index.js │ │ │ │ └── style.css │ │ │ ├── caldav │ │ │ │ ├── CalDAV.js │ │ │ │ ├── account-page │ │ │ │ │ ├── AccountTab.css │ │ │ │ │ ├── AccountTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ ├── share-page │ │ │ │ │ ├── ShareTab.css │ │ │ │ │ ├── ShareTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ └── sync-page │ │ │ │ │ ├── SyncTab.css │ │ │ │ │ ├── SyncTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ ├── callmebot │ │ │ │ ├── CallMeBot.jsx │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ └── index.js │ │ │ ├── enedis-gateway │ │ │ │ ├── EnedisPage.jsx │ │ │ │ ├── UsagePoints.jsx │ │ │ │ ├── Welcome.jsx │ │ │ │ ├── consts.js │ │ │ │ └── enedis-button.png │ │ │ ├── ewelink │ │ │ │ ├── EweLinkDeviceBox.jsx │ │ │ │ ├── EweLinkPage.jsx │ │ │ │ ├── actions.js │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── edit-page │ │ │ │ │ └── index.js │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ └── index.js │ │ │ ├── free-mobile │ │ │ │ ├── FreeMobile.jsx │ │ │ │ ├── actions.js │ │ │ │ └── index.js │ │ │ ├── google-cast │ │ │ │ ├── GoogleCastDeviceBox.jsx │ │ │ │ ├── GoogleCastPage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ └── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ ├── google-home-gateway │ │ │ │ ├── Layout.jsx │ │ │ │ ├── index.js │ │ │ │ ├── style.css │ │ │ │ └── welcome.jsx │ │ │ ├── homekit │ │ │ │ ├── HomeKit.jsx │ │ │ │ ├── actions.js │ │ │ │ ├── index.js │ │ │ │ └── style.css │ │ │ ├── lan-manager │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── LANManagerPage.js │ │ │ │ ├── device-page │ │ │ │ │ ├── LANManagerDevice.jsx │ │ │ │ │ ├── LANManagerDeviceTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ ├── discover-page │ │ │ │ │ ├── LANManagerDiscoverDevice.jsx │ │ │ │ │ ├── LANManagerDiscoverTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ └── index.js │ │ │ │ ├── settings-page │ │ │ │ │ ├── LANManagerIPLine.jsx │ │ │ │ │ ├── LANManagerIPRange.jsx │ │ │ │ │ ├── LANManagerPresenceScanner.jsx │ │ │ │ │ ├── LANManagerSettingsTab.jsx │ │ │ │ │ └── index.js │ │ │ │ └── style.css │ │ │ ├── matter │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── MatterDeviceBox.jsx │ │ │ │ ├── MatterDevices.jsx │ │ │ │ ├── MatterDiscoverPage.jsx │ │ │ │ ├── MatterPage.jsx │ │ │ │ ├── MatterSettingsPage.jsx │ │ │ │ └── style.css │ │ │ ├── melcloud │ │ │ │ ├── MELCloudDeviceBox.jsx │ │ │ │ ├── MELCloudPage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── edit-page │ │ │ │ │ └── index.js │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ └── index.js │ │ │ ├── mqtt │ │ │ │ ├── MqttPage.js │ │ │ │ ├── commons │ │ │ │ │ ├── CheckMqttPanel.js │ │ │ │ │ └── actions.js │ │ │ │ ├── debug-page │ │ │ │ │ └── Debug.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── Device.jsx │ │ │ │ │ ├── DeviceForm.jsx │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── setup │ │ │ │ │ │ ├── Feature.jsx │ │ │ │ │ │ ├── FeatureTab.jsx │ │ │ │ │ │ └── index.js │ │ │ │ │ └── style.css │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupBrokerContainer.jsx │ │ │ │ │ ├── SetupForm.jsx │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ └── index.js │ │ │ ├── netatmo │ │ │ │ ├── NetatmoDeviceBox.jsx │ │ │ │ ├── NetatmoPage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── StateConnection.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── StateConnection.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── setup-page │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ ├── StateConnection.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ └── style.css │ │ │ ├── nextcloud-talk │ │ │ │ ├── NextcloudTalk.jsx │ │ │ │ ├── actions.js │ │ │ │ └── index.js │ │ │ ├── node-red │ │ │ │ ├── NodeRedPage.js │ │ │ │ └── setup-page │ │ │ │ │ ├── CheckStatus.js │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ ├── openai │ │ │ │ ├── Layout.jsx │ │ │ │ └── index.js │ │ │ ├── openweather │ │ │ │ ├── OpenWeather.jsx │ │ │ │ ├── actions.js │ │ │ │ └── index.js │ │ │ ├── owntracks │ │ │ │ └── welcome.jsx │ │ │ ├── philips-hue │ │ │ │ ├── PhilipsHuePage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── Device.jsx │ │ │ │ │ ├── DeviceForm.jsx │ │ │ │ │ ├── DevicePage.jsx │ │ │ │ │ ├── FoundDevices.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ ├── rtsp-camera │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── RtspCamera.jsx │ │ │ │ ├── RtspCameraBox.jsx │ │ │ │ ├── actions.js │ │ │ │ ├── index.js │ │ │ │ └── style.css │ │ │ ├── sonos │ │ │ │ ├── SonosDeviceBox.jsx │ │ │ │ ├── SonosPage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ └── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ ├── tasmota │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── TasmotaDeviceBox.jsx │ │ │ │ ├── TasmotaPage.js │ │ │ │ ├── actions.js │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── discover-http │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── SearchForm.jsx │ │ │ │ │ └── index.js │ │ │ │ ├── discover-mqtt │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ └── index.js │ │ │ │ ├── edit-page │ │ │ │ │ └── index.js │ │ │ │ └── style.css │ │ │ ├── telegram │ │ │ │ ├── Telegram.jsx │ │ │ │ ├── actions.js │ │ │ │ └── index.js │ │ │ ├── tp-link │ │ │ │ ├── TpLinkPage.jsx │ │ │ │ └── device-page │ │ │ │ │ ├── Device.jsx │ │ │ │ │ ├── DeviceForm.jsx │ │ │ │ │ ├── DevicePage.jsx │ │ │ │ │ ├── FoundDevices.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ ├── tuya │ │ │ │ ├── TuyaDeviceBox.jsx │ │ │ │ ├── TuyaPage.jsx │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── edit-page │ │ │ │ │ └── index.js │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ └── index.js │ │ │ ├── xiaomi │ │ │ │ ├── Device.jsx │ │ │ │ ├── DevicePanel.jsx │ │ │ │ ├── SetupPanel.jsx │ │ │ │ ├── XiaomiLayout.jsx │ │ │ │ ├── XiaomiSensor.jsx │ │ │ │ ├── actions.js │ │ │ │ ├── edit-page │ │ │ │ │ ├── EditPage.jsx │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── style.css │ │ │ ├── zigbee2mqtt │ │ │ │ ├── Zigbee2mqttPage.js │ │ │ │ ├── commons │ │ │ │ │ ├── CheckStatus.js │ │ │ │ │ └── actions.js │ │ │ │ ├── device-page │ │ │ │ │ ├── DeviceTab.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── Zigbee2mqttBox.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── discover-page │ │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ │ ├── DiscoveredBox.jsx │ │ │ │ │ ├── EmptyState.jsx │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── style.css │ │ │ │ ├── edit-page │ │ │ │ │ └── index.js │ │ │ │ └── setup-page │ │ │ │ │ ├── SetupModePanel.jsx │ │ │ │ │ ├── SetupPanel.jsx │ │ │ │ │ ├── SetupTab.jsx │ │ │ │ │ ├── components │ │ │ │ │ ├── ContainerLinkStatus.jsx │ │ │ │ │ ├── ContainerStatus.jsx │ │ │ │ │ ├── InstallationCard.jsx │ │ │ │ │ ├── Requirement.jsx │ │ │ │ │ └── SubmitConfiguration.jsx │ │ │ │ │ ├── constants.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── local │ │ │ │ │ ├── SetupLocalMode.jsx │ │ │ │ │ ├── SetupLocalModeCard.jsx │ │ │ │ │ ├── SetupLocalOptions.jsx │ │ │ │ │ └── SetupLocalSummary.jsx │ │ │ │ │ ├── remote │ │ │ │ │ ├── SetupRemoteMode.jsx │ │ │ │ │ ├── SetupRemoteModeCard.jsx │ │ │ │ │ ├── SetupRemoteOptions.jsx │ │ │ │ │ └── SetupRemoteSummary.jsx │ │ │ │ │ └── status │ │ │ │ │ ├── EnableStatus.jsx │ │ │ │ │ └── RunningStatus.jsx │ │ │ └── zwavejs-ui │ │ │ │ ├── ZwaveJSUIDeviceBox.jsx │ │ │ │ ├── ZwaveJSUIPage.jsx │ │ │ │ ├── device-page │ │ │ │ ├── DeviceTab.jsx │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── index.js │ │ │ │ └── style.css │ │ │ │ ├── discover-page │ │ │ │ ├── DiscoverTab.jsx │ │ │ │ ├── EmptyState.jsx │ │ │ │ ├── index.js │ │ │ │ └── style.css │ │ │ │ └── setup-page │ │ │ │ └── index.js │ │ └── index.js │ ├── locked │ │ ├── index.js │ │ └── style.css │ ├── login-gateway │ │ ├── LoginGatewayPage.jsx │ │ └── index.js │ ├── login │ │ ├── LoginPage.jsx │ │ └── index.js │ ├── map │ │ ├── ColorPicker.jsx │ │ ├── Map.jsx │ │ ├── NewArea.jsx │ │ ├── NewAreaMap.jsx │ │ ├── index.jsx │ │ └── style.css │ ├── profile │ │ ├── DashboardProfilePage.jsx │ │ ├── EditProfile.jsx │ │ └── index.js │ ├── reset-password │ │ ├── ResetPasswordForm.jsx │ │ ├── ResetPasswordPage.jsx │ │ ├── ResetPasswordSuccess.jsx │ │ └── index.js │ ├── scene │ │ ├── EmptyState.jsx │ │ ├── SceneCard.jsx │ │ ├── SceneCards.jsx │ │ ├── ScenePage.jsx │ │ ├── SceneTagFilter.jsx │ │ ├── constant.js │ │ ├── duplicate-scene │ │ │ ├── DuplicateScenePage.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ ├── edit-scene │ │ │ ├── ActionCard.jsx │ │ │ ├── ActionGroup.jsx │ │ │ ├── EditActions.jsx │ │ │ ├── EditScenePage.jsx │ │ │ ├── EmptyDropZone.jsx │ │ │ ├── Settings.jsx │ │ │ ├── TriggerCard.jsx │ │ │ ├── TriggerGroup.jsx │ │ │ ├── actions │ │ │ │ ├── AskAI.jsx │ │ │ │ ├── BlinkLightParams.jsx │ │ │ │ ├── CalendarIsEventRunning.css │ │ │ │ ├── CalendarIsEventRunning.jsx │ │ │ │ ├── CheckAlarmMode.jsx │ │ │ │ ├── CheckTime.css │ │ │ │ ├── CheckTime.jsx │ │ │ │ ├── CheckUserPresence.jsx │ │ │ │ ├── ChooseActionTypeCard.jsx │ │ │ │ ├── ConditionIfElseThen.jsx │ │ │ │ ├── DelayActionParams.jsx │ │ │ │ ├── DeviceGetValueParams.jsx │ │ │ │ ├── DeviceSetValue.css │ │ │ │ ├── DeviceSetValue.jsx │ │ │ │ ├── EcowattCondition.jsx │ │ │ │ ├── EdfTempoCondition.jsx │ │ │ │ ├── HouseEmptyOrNotCondition.jsx │ │ │ │ ├── HttpRequest.jsx │ │ │ │ ├── PlayNotification.jsx │ │ │ │ ├── SendMessageCameraParams.jsx │ │ │ │ ├── SendMessageParams.jsx │ │ │ │ ├── SendMqttMessage.jsx │ │ │ │ ├── SendSms.jsx │ │ │ │ ├── SendZigbee2MqttMessage.jsx │ │ │ │ ├── SetAlarmMode.jsx │ │ │ │ ├── StartSceneParams.jsx │ │ │ │ ├── TurnOnOffLightParams.jsx │ │ │ │ ├── TurnOnOffSwitchParams.jsx │ │ │ │ ├── UserPresence.jsx │ │ │ │ └── only-continue-if │ │ │ │ │ ├── Condition.css │ │ │ │ │ ├── Condition.jsx │ │ │ │ │ └── OnlyContinueIfParams.jsx │ │ │ ├── index.js │ │ │ ├── sceneUtils.js │ │ │ ├── style.css │ │ │ └── triggers │ │ │ │ ├── AlarmModeTrigger.jsx │ │ │ │ ├── CalendarEventIsComing.jsx │ │ │ │ ├── ChooseTriggerTypeCard.jsx │ │ │ │ ├── DeviceFeatureState.jsx │ │ │ │ ├── GladysStartTrigger.jsx │ │ │ │ ├── HouseEmptyOrNot.jsx │ │ │ │ ├── MQTTReceivedTrigger.jsx │ │ │ │ ├── ScheduledTrigger.jsx │ │ │ │ ├── SunriseSunsetTrigger.jsx │ │ │ │ ├── UserEnteredOrLeftArea.jsx │ │ │ │ ├── UserPresenceTrigger.jsx │ │ │ │ ├── device-states │ │ │ │ ├── BinaryDeviceState.jsx │ │ │ │ ├── ButtonClickDeviceState.jsx │ │ │ │ ├── DefaultDeviceState.jsx │ │ │ │ ├── LevelSensorDeviceState.jsx │ │ │ │ ├── PilotWireModeDeviceState.jsx │ │ │ │ ├── PresenceSensorDeviceState.jsx │ │ │ │ └── ThresholdDeviceState.jsx │ │ │ │ └── style.css │ │ ├── index.js │ │ ├── new-scene │ │ │ ├── NewScenePage.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ └── style.css │ ├── settings │ │ ├── SettingsLayout.jsx │ │ ├── settings-background-jobs │ │ │ ├── JobList.jsx │ │ │ ├── SettingsBackgroundJobs.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ ├── settings-backup │ │ │ ├── GatewayBackupList.jsx │ │ │ ├── GatewayBackupPage.jsx │ │ │ ├── GatewayBackupRow.jsx │ │ │ ├── GatewayNotConfigured.jsx │ │ │ ├── GatewayRestoreInProgress.jsx │ │ │ ├── UpgradePlan.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ ├── settings-billing │ │ │ ├── GatewayBilling.jsx │ │ │ └── index.js │ │ ├── settings-gateway-open-api │ │ │ ├── OpenApi.js │ │ │ ├── OpenApiKey.js │ │ │ └── index.js │ │ ├── settings-gateway-users │ │ │ ├── UserList.js │ │ │ ├── UserRow.js │ │ │ └── index.js │ │ ├── settings-gateway │ │ │ ├── GatewayBackupKey.jsx │ │ │ ├── GatewayConfigured.jsx │ │ │ ├── GatewayConnectedSuccess.jsx │ │ │ ├── GatewayDisconnect.jsx │ │ │ ├── GatewayPage.jsx │ │ │ ├── GatewayPricing.jsx │ │ │ ├── GatewayUserRow.jsx │ │ │ ├── GatewayUsersList.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ ├── settings-house │ │ │ ├── EmptySearch.jsx │ │ │ ├── House.jsx │ │ │ ├── HousePage.jsx │ │ │ └── index.js │ │ ├── settings-service │ │ │ ├── ServiceItem.jsx │ │ │ ├── ServicesPage.jsx │ │ │ └── index.js │ │ ├── settings-session │ │ │ ├── SessionDevice.jsx │ │ │ ├── SessionDeviceLabel.jsx │ │ │ ├── SessionsPage.jsx │ │ │ └── index.js │ │ ├── settings-system │ │ │ ├── SettingsSystemBatteryLevelWarning.jsx │ │ │ ├── SettingsSystemContainers.jsx │ │ │ ├── SettingsSystemDatabaseCleaning.jsx │ │ │ ├── SettingsSystemDuckDbMigration.jsx │ │ │ ├── SettingsSystemKeepDeviceHistory.jsx │ │ │ ├── SettingsSystemOperations.jsx │ │ │ ├── SettingsSystemPage.jsx │ │ │ ├── SettingsSystemTimeExpiryState.jsx │ │ │ ├── SettingsSystemTimezone.jsx │ │ │ ├── index.js │ │ │ └── style.css │ │ └── settings-users │ │ │ ├── UserCard.jsx │ │ │ ├── UserPage.jsx │ │ │ ├── create-user │ │ │ ├── CreateUserPage.js │ │ │ └── index.js │ │ │ ├── edit-user │ │ │ ├── EditUserPage.jsx │ │ │ ├── ResetPassword.jsx │ │ │ └── index.js │ │ │ └── index.js │ ├── signup-gateway │ │ ├── SignupBase.js │ │ ├── SignupForm.js │ │ ├── SignupGeneratingKeys.js │ │ ├── index.js │ │ └── spinner.css │ └── signup │ │ ├── 1-welcome │ │ ├── WelcomeTab.jsx │ │ └── index.js │ │ ├── 2-create-account-gladys-gateway │ │ ├── RestoreBackup.jsx │ │ ├── RestoreBackupRow.jsx │ │ ├── RestoreInProgress.jsx │ │ ├── SetRestoreKey.jsx │ │ └── index.js │ │ ├── 2-create-account-local │ │ ├── CreateAccountLocalTab.jsx │ │ └── index.js │ │ ├── 3-preferences │ │ ├── PreferencesTab.jsx │ │ └── index.js │ │ ├── 4-configure-house │ │ ├── ConfigureHouseTab.jsx │ │ └── index.js │ │ ├── 5-success │ │ ├── SuccessTab.jsx │ │ └── index.js │ │ ├── layout.jsx │ │ └── style.css │ ├── style │ └── index.css │ ├── template.html │ └── utils │ ├── DemoHttpClient.js │ ├── DemoSession.js │ ├── Dispatcher.js │ ├── GatewayHttpClient.js │ ├── GatewaySession.js │ ├── HttpClient.js │ ├── Session.js │ ├── bytesFormat.js │ ├── color.js │ ├── consts.js │ ├── date.js │ ├── device.js │ ├── getDefaultState.js │ ├── keyValueStore.js │ ├── mergeArray.js │ ├── picture.js │ ├── slugify.js │ ├── url.js │ ├── validateEmail.js │ ├── validator.js │ └── withIntlAsProp.js ├── insomnia.json ├── package-lock.json ├── package.json └── server ├── .eslintignore ├── .eslintrc.json ├── .prettierignore ├── .prettierrc.json ├── api ├── controllers │ ├── area.controller.js │ ├── calendar.controller.js │ ├── camera.controller.js │ ├── dashboard.controller.js │ ├── device.controller.js │ ├── gateway.controller.js │ ├── house.controller.js │ ├── http.controller.js │ ├── job.controller.js │ ├── light.controller.js │ ├── location.controller.js │ ├── message.controller.js │ ├── ping.controller.js │ ├── room.controller.js │ ├── scene.controller.js │ ├── service.controller.js │ ├── session.controller.js │ ├── system.controller.js │ ├── user.controller.js │ ├── variable.controller.js │ └── weather.controller.js ├── index.js ├── middlewares │ ├── adminMiddleware.js │ ├── asyncMiddleware.js │ ├── authMiddleware.js │ ├── corsMiddleware.js │ ├── errorMiddleware.js │ ├── isInstanceConfigured.js │ ├── notFoundMiddleware.js │ └── rateLimitMiddleware.js ├── routes.js ├── setupGateway.js ├── setupRoutes.js └── websockets │ └── index.js ├── cli └── install_service_dependencies.js ├── config ├── brain │ ├── backup │ │ ├── answers.en.json │ │ └── answers.fr.json │ ├── battery-threshold │ │ ├── answers.en.json │ │ └── answers.fr.json │ ├── calendar │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── camera │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── chat │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── humidity-sensor │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── index.js │ ├── light │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── openai │ │ ├── answers.en.json │ │ └── answers.fr.json │ ├── scene │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── switch │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── system │ │ ├── answers.en.json │ │ └── answers.fr.json │ ├── taxi │ │ └── questions.en.json │ ├── temperature-sensor │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json │ ├── user │ │ ├── answers.en.json │ │ └── answers.fr.json │ └── weather │ │ ├── answers.en.json │ │ ├── answers.fr.json │ │ ├── questions.en.json │ │ └── questions.fr.json ├── config.js ├── default-profile-picture.b64 ├── icons.json └── scheduler-jobs.js ├── index.js ├── jsconfig.json ├── lib ├── area │ ├── area.checkNewLocation.js │ ├── area.create.js │ ├── area.destroy.js │ ├── area.get.js │ ├── area.getBySelector.js │ ├── area.init.js │ ├── area.update.js │ └── index.js ├── brain │ ├── brain.addNamedEntity.js │ ├── brain.classify.js │ ├── brain.getEntityIdByName.js │ ├── brain.getReply.js │ ├── brain.load.js │ ├── brain.removeNamedEntity.js │ ├── brain.train.js │ └── index.js ├── calendar │ ├── calendar.create.js │ ├── calendar.createEvent.js │ ├── calendar.destroy.js │ ├── calendar.destroyEvent.js │ ├── calendar.destroyEvents.js │ ├── calendar.findCurrentlyRunningEvent.js │ ├── calendar.get.js │ ├── calendar.getEvents.js │ ├── calendar.update.js │ ├── calendar.updateEvent.js │ └── index.js ├── dashboard │ ├── dashboard.create.js │ ├── dashboard.destroy.js │ ├── dashboard.get.js │ ├── dashboard.getBySelector.js │ ├── dashboard.update.js │ ├── dashboard.updateOrder.js │ └── index.js ├── device │ ├── camera │ │ ├── camera.command.js │ │ ├── camera.get.js │ │ ├── camera.getImage.js │ │ ├── camera.getImagesInRoom.js │ │ ├── camera.getLiveImage.js │ │ ├── camera.setImage.js │ │ └── index.js │ ├── device.add.js │ ├── device.addFeature.js │ ├── device.addParam.js │ ├── device.checkBatteries.js │ ├── device.create.js │ ├── device.destroy.js │ ├── device.get.js │ ├── device.getBySelector.js │ ├── device.getDeviceFeaturesAggregates.js │ ├── device.getDeviceFeaturesAggregatesMulti.js │ ├── device.getDuckDbMigrationState.js │ ├── device.init.js │ ├── device.migrateFromSQLiteToDuckDb.js │ ├── device.newStateEvent.js │ ├── device.notify.js │ ├── device.onPurgeStatesEvent.js │ ├── device.poll.js │ ├── device.pollAll.js │ ├── device.purgeAllSqliteStates.js │ ├── device.purgeStates.js │ ├── device.purgeStatesByFeatureId.js │ ├── device.saveHistoricalState.js │ ├── device.saveState.js │ ├── device.saveStringState.js │ ├── device.setParam.js │ ├── device.setValue.js │ ├── device.setupPoll.js │ ├── humidity-sensor │ │ ├── humidity-sensor.command.js │ │ ├── humidity-sensor.getHumidityInRoom.js │ │ └── index.js │ ├── index.js │ ├── light │ │ ├── index.js │ │ ├── light.buildLightObject.js │ │ ├── light.command.js │ │ ├── light.getLightsInRoom.js │ │ ├── light.init.js │ │ ├── light.turnOff.js │ │ └── light.turnOn.js │ ├── switch │ │ ├── index.js │ │ └── switch.command.js │ └── temperature-sensor │ │ ├── index.js │ │ ├── temperature-sensor.command.js │ │ └── temperature-sensor.getTemperatureInRoom.js ├── event │ └── index.js ├── gateway │ ├── enedis │ │ ├── gateway.enedisGetConsumptionLoadCurve.js │ │ ├── gateway.enedisGetDailyConsumption.js │ │ └── gateway.enedisGetDailyConsumptionMaxPower.js │ ├── gateway.backup.js │ ├── gateway.checkIfBackupNeeded.js │ ├── gateway.disconnect.js │ ├── gateway.downloadBackup.js │ ├── gateway.forwardDeviceStateToAlexa.js │ ├── gateway.forwardDeviceStateToGoogleHome.js │ ├── gateway.forwardMessageToOpenAI.js │ ├── gateway.forwardWebsockets.js │ ├── gateway.getBackups.js │ ├── gateway.getEcowattSignals.js │ ├── gateway.getEdfTempo.js │ ├── gateway.getInstanceKeysFingerprint.js │ ├── gateway.getLatestGladysVersion.js │ ├── gateway.getStatus.js │ ├── gateway.getTTSApiUrl.js │ ├── gateway.getUsersKeys.js │ ├── gateway.handleAlexaMessage.js │ ├── gateway.handleGoogleHomeMessage.js │ ├── gateway.handleNewMessage.js │ ├── gateway.init.js │ ├── gateway.login.js │ ├── gateway.loginTwoFactor.js │ ├── gateway.openAIAsk.js │ ├── gateway.refreshUserKeys.js │ ├── gateway.restoreBackup.js │ ├── gateway.restoreBackupEvent.js │ ├── gateway.saveUsersKeys.js │ └── index.js ├── house │ ├── house.arm.js │ ├── house.create.js │ ├── house.destroy.js │ ├── house.disarm.js │ ├── house.disarmWithCode.js │ ├── house.get.js │ ├── house.getBySelector.js │ ├── house.getRooms.js │ ├── house.getUsersAtHome.js │ ├── house.isEmpty.js │ ├── house.panic.js │ ├── house.partialArm.js │ ├── house.update.js │ ├── house.userLeft.js │ ├── house.userSeen.js │ └── index.js ├── http │ ├── http.request.js │ └── index.js ├── index.js ├── job │ ├── index.js │ ├── job.finish.js │ ├── job.get.js │ ├── job.init.js │ ├── job.purge.js │ ├── job.start.js │ ├── job.updateProgress.js │ └── job.wrapper.js ├── location │ ├── index.js │ ├── location.create.js │ ├── location.get.js │ ├── location.getLast.js │ └── location.handleNewGatewayOwntracksLocation.js ├── message │ ├── index.js │ ├── message.create.js │ ├── message.get.js │ ├── message.handleEvent.js │ ├── message.purge.js │ ├── message.reply.js │ ├── message.replyByIntent.js │ └── message.sendToUser.js ├── room │ ├── index.js │ ├── room.create.js │ ├── room.destroy.js │ ├── room.get.js │ ├── room.getAll.js │ ├── room.getBySelector.js │ ├── room.init.js │ └── room.update.js ├── scene │ ├── index.js │ ├── scene.actions.js │ ├── scene.addScene.js │ ├── scene.cancelTriggers.js │ ├── scene.checkCalendarTriggers.js │ ├── scene.checkTrigger.js │ ├── scene.command.js │ ├── scene.create.js │ ├── scene.dailyUpdate.js │ ├── scene.destroy.js │ ├── scene.duplicate.js │ ├── scene.execute.js │ ├── scene.executeActions.js │ ├── scene.executeSingleAction.js │ ├── scene.get.js │ ├── scene.getBySelector.js │ ├── scene.getTag.js │ ├── scene.init.js │ ├── scene.triggers.js │ └── scene.update.js ├── scheduler │ ├── index.js │ ├── scheduler.cancelJob.js │ ├── scheduler.init.js │ └── scheduler.scheduleJob.js ├── service │ ├── index.js │ ├── service.getAll.js │ ├── service.getByName.js │ ├── service.getLocalServiceByName.js │ ├── service.getMessageServices.js │ ├── service.getService.js │ ├── service.getServiceById.js │ ├── service.getServices.js │ ├── service.getUsage.js │ ├── service.load.js │ ├── service.start.js │ ├── service.startAll.js │ └── service.stop.js ├── session │ ├── index.js │ ├── session.create.js │ ├── session.createApiKey.js │ ├── session.get.js │ ├── session.getAccessToken.js │ ├── session.getTabletMode.js │ ├── session.revoke.js │ ├── session.setTabletMode.js │ ├── session.setTabletModeLocked.js │ ├── session.unlockTabletMode.js │ ├── session.validateAccessToken.js │ └── session.validateApiKey.js ├── state │ ├── Store.js │ └── index.js ├── system │ ├── index.js │ ├── system.checkIfGladysUpgraded.js │ ├── system.createContainer.js │ ├── system.exec.js │ ├── system.getContainerMounts.js │ ├── system.getContainers.js │ ├── system.getDiskSpace.js │ ├── system.getGladysBasePath.js │ ├── system.getGladysContainerId.js │ ├── system.getInfos.js │ ├── system.getNetworkMode.js │ ├── system.init.js │ ├── system.inspectContainer.js │ ├── system.installUpgrade.js │ ├── system.isDocker.js │ ├── system.pull.js │ ├── system.removeContainer.js │ ├── system.restartContainer.js │ ├── system.saveLatestGladysVersion.js │ ├── system.shutdown.js │ ├── system.stopContainer.js │ └── system.vacuum.js ├── user │ ├── index.js │ ├── user.create.js │ ├── user.destroy.js │ ├── user.forgotPassword.js │ ├── user.get.js │ ├── user.getById.js │ ├── user.getByRole.js │ ├── user.getBySelector.js │ ├── user.getByTelegramUserId.js │ ├── user.getPicture.js │ ├── user.getUserCount.js │ ├── user.init.js │ ├── user.login.js │ ├── user.update.js │ ├── user.updateBySelector.js │ └── user.updatePassword.js ├── variable │ ├── index.js │ ├── variable.destroy.js │ ├── variable.getValue.js │ ├── variable.getVariables.js │ └── variable.setValue.js └── weather │ ├── index.js │ ├── weather.command.js │ ├── weather.error.js │ └── weather.get.js ├── migrations ├── 20190205063641-create-user.js ├── 20190206102938-create-location.js ├── 20190206114851-create-house.js ├── 20190211033038-create-life-event.js ├── 20190211034727-create-room.js ├── 20190211035101-create-device.js ├── 20190211035238-create-device-feature.js ├── 20190211041243-create-device-feature-state.js ├── 20190211042223-create-calendar.js ├── 20190211042644-create-calendar-event.js ├── 20190211043231-create-pod.js ├── 20190211043515-create-service.js ├── 20190211043957-create-variable.js ├── 20190211044205-create-script.js ├── 20190211044442-create-area.js ├── 20190211044839-create-dashboard.js ├── 20190211045110-create-scene.js ├── 20190211045641-create-trigger.js ├── 20190211050844-trigger_scene.js ├── 20190211051215-create-message.js ├── 20190212043623-create-session.js ├── 20190318084429-create-device-param.js ├── 20200123094438-add-triggers-attribute.js ├── 20200201125436-add-caldav-data.js ├── 20200207214849-add-calendar-event-url.js ├── 20200513195013-session-with-useragent.js ├── 20201018084535-service-with-status.js ├── 20201031091221-service-reword-status.js ├── 20201128203008-add-color-caldav.js ├── 20201220100508-service-remove-not-configured-status.js ├── 20210129155044-multi-user.js ├── 20210705025615-add-active-in-scene.js ├── 20210723014058-add-device-feature-state-aggregate.js ├── 20210726050004-add-jobs-table.js ├── 20211023172755-share-calendars.js ├── 20211204080815-clean-nan-device-states.js ├── 20220322170955-clean-nan-device-states-again.js ├── 20220503155531-clean-nan-device-states-aggregate.js ├── 20220513204058-add-calendar-type-event-desc.js ├── 20220912140232-add-index-device-feature-state.js ├── 20230102171731-update-tasmota-energy-features.js ├── 20230130044921-add-position-dashboard.js ├── 20230410123029-broadlink-change-volume-feature.js ├── 20230414020652-enedis-unit-is-watt-hour.js ├── 20230428064822-translate-enedis-name.js ├── 20230511161620-description-scene.js ├── 20230518062954-enedis-reset-aggregate.js ├── 20230628144609-change-rotation-camera.js ├── 20230928144012-add-tag-scene.js ├── 20230929085337-alarm-mode.js ├── 20231115163530-default-system-variable-device-battery.js ├── 20240211093530-default-netatmo-variable-api.js ├── 20240513141123-dashboard-visibility.js ├── 20241105200700-update-lixee-tic-features.js └── 20241111211825-update-lixee-tic-selector_externalId.js ├── models ├── area.js ├── calendar.js ├── calendar_event.js ├── dashboard.js ├── device.js ├── device_feature.js ├── device_feature_state.js ├── device_feature_state_aggregate.js ├── device_param.js ├── house.js ├── index.js ├── job.js ├── life_event.js ├── location.js ├── message.js ├── pod.js ├── room.js ├── scene.js ├── script.js ├── service.js ├── session.js ├── tag_scene.js ├── user.js └── variable.js ├── package-lock.json ├── package.json ├── seeders ├── 20190205070000-house.js ├── 20190205071039-demo-user.js ├── 20190211053203-demo-location.js ├── 20190219041452-message.js ├── 20190226025926-service.js ├── 20190226025931-variable.js ├── 20190227043234-scene.js ├── 20190227081653-room.js ├── 20190227081656-device.js ├── 20190227081700-device-feature.js ├── 20190401042124-session.js ├── 20190403081927-calendar.js ├── 20190403081933-calendar-event.js ├── 20190416085240-device-param.js ├── 20190506074218-dashboard.js ├── 20190507083413-area.js └── 20231002113233-tag-scene.js ├── services ├── airplay │ ├── api │ │ └── airplay.controller.js │ ├── index.js │ ├── lib │ │ ├── airplay.init.js │ │ ├── airplay.scan.js │ │ ├── airplay.setValue.js │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ └── convertToGladysDevice.js ├── alexa │ ├── index.js │ ├── lib │ │ ├── alexa.constants.js │ │ ├── alexa.onDiscovery.js │ │ ├── alexa.onExecute.js │ │ ├── alexa.onReportState.js │ │ ├── deviceMappings.js │ │ ├── index.js │ │ └── syncDeviceConverter.js │ ├── package-lock.json │ └── package.json ├── bluetooth │ ├── README.md │ ├── api │ │ └── bluetooth.controller.js │ ├── index.js │ ├── lib │ │ ├── commands │ │ │ ├── bluetooth.applyOnPeripheral.js │ │ │ ├── bluetooth.completeDevice.js │ │ │ ├── bluetooth.getCharacteristic.js │ │ │ ├── bluetooth.getDiscoveredDevice.js │ │ │ ├── bluetooth.getDiscoveredDevices.js │ │ │ ├── bluetooth.getPeripheral.js │ │ │ ├── bluetooth.getStatus.js │ │ │ ├── bluetooth.readDevice.js │ │ │ ├── bluetooth.scan.js │ │ │ ├── bluetooth.scanDevice.js │ │ │ ├── bluetooth.scanPresence.js │ │ │ ├── bluetooth.start.js │ │ │ ├── bluetooth.stop.js │ │ │ ├── bluetooth.stopScanPresence.js │ │ │ ├── bluetooth.subscribeDevice.js │ │ │ ├── bluetooth.unsubscribeDevice.js │ │ │ └── bluetooth.writeDevice.js │ │ ├── config │ │ │ ├── bluetooth.getConfiguration.js │ │ │ ├── bluetooth.initPresenceScanner.js │ │ │ └── bluetooth.saveConfiguration.js │ │ ├── device │ │ │ ├── bluetooth.information.js │ │ │ └── bluetooth.transformToDevice.js │ │ ├── events │ │ │ ├── bluetooth.broadcastStatus.js │ │ │ ├── bluetooth.discover.js │ │ │ ├── bluetooth.scanStart.js │ │ │ ├── bluetooth.scanStop.js │ │ │ └── bluetooth.stateChange.js │ │ ├── index.js │ │ └── utils │ │ │ ├── bluetooth.constants.js │ │ │ ├── characteristic │ │ │ ├── bluetooth.read.js │ │ │ ├── bluetooth.subscribe.js │ │ │ ├── bluetooth.unsubscribe.js │ │ │ └── bluetooth.write.js │ │ │ ├── peripheral │ │ │ ├── bluetooth.connect.js │ │ │ └── bluetooth.discoverServices.js │ │ │ └── service │ │ │ └── bluetooth.discoverCharacteristics.js │ ├── package-lock.json │ └── package.json ├── broadlink │ ├── api │ │ └── broadlink.controller.js │ ├── index.js │ ├── lib │ │ ├── commands │ │ │ ├── broadlink.addPeripheral.js │ │ │ ├── broadlink.buildPeripheral.js │ │ │ ├── broadlink.getDevice.js │ │ │ ├── broadlink.getPeripherals.js │ │ │ ├── broadlink.init.js │ │ │ ├── broadlink.loadMapper.js │ │ │ ├── broadlink.poll.js │ │ │ ├── broadlink.setValue.js │ │ │ ├── broadlink.stop.js │ │ │ └── features │ │ │ │ ├── broadlink.light.js │ │ │ │ ├── broadlink.remote.js │ │ │ │ ├── broadlink.sensor.js │ │ │ │ ├── broadlink.switch.js │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── learn │ │ │ ├── broadlink.cancelLearn.js │ │ │ ├── broadlink.checkData.js │ │ │ ├── broadlink.learn.js │ │ │ └── broadlink.send.js │ │ └── utils │ │ │ └── broadlink.constants.js │ ├── package-lock.json │ └── package.json ├── caldav │ ├── api │ │ └── caldav.controller.js │ ├── index.js │ ├── lib │ │ ├── calendar │ │ │ ├── calendar.cleanUp.js │ │ │ ├── calendar.disableCalendar.js │ │ │ ├── calendar.enableCalendar.js │ │ │ ├── calendar.formaters.js │ │ │ ├── calendar.requests.js │ │ │ ├── calendar.syncUserCalendars.js │ │ │ └── calendar.syncUserWebcals.js │ │ ├── config │ │ │ └── index.js │ │ └── index.js │ ├── package-lock.json │ └── package.json ├── callmebot │ ├── index.js │ ├── lib │ │ ├── index.js │ │ └── message.send.js │ ├── package-lock.json │ └── package.json ├── ecowatt │ ├── controllers │ │ └── ecowatt.controller.js │ ├── index.js │ ├── package-lock.json │ └── package.json ├── edf-tempo │ ├── controllers │ │ └── edf-tempo.controller.js │ ├── index.js │ ├── package-lock.json │ └── package.json ├── enedis │ ├── api │ │ └── enedis.controller.js │ ├── index.js │ ├── lib │ │ ├── enedis.init.js │ │ ├── enedis.sync.js │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ └── parser.js ├── ewelink │ ├── api │ │ └── ewelink.controller.js │ ├── index.js │ ├── lib │ │ ├── device │ │ │ ├── connect.js │ │ │ ├── discover.js │ │ │ ├── index.js │ │ │ ├── poll.js │ │ │ ├── setValue.js │ │ │ └── status.js │ │ ├── features │ │ │ ├── binary.js │ │ │ ├── humidity.js │ │ │ ├── index.js │ │ │ └── temperature.js │ │ └── utils │ │ │ ├── constants.js │ │ │ └── externalId.js │ ├── package-lock.json │ └── package.json ├── example │ ├── README.md │ ├── index.js │ ├── lib │ │ └── light │ │ │ ├── index.js │ │ │ ├── light.getState.js │ │ │ ├── light.setValue.js │ │ │ ├── light.turnOff.js │ │ │ └── light.turnOn.js │ ├── package-lock.json │ └── package.json ├── free-mobile │ ├── index.js │ ├── package-lock.json │ └── package.json ├── google-actions │ ├── index.js │ ├── lib │ │ ├── deviceTypes │ │ │ ├── googleActions.curtain.type.js │ │ │ ├── googleActions.light.type.js │ │ │ ├── googleActions.shutter.type.js │ │ │ ├── googleActions.switch.type.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── smarthome │ │ │ ├── googleActions.onExecute.js │ │ │ ├── googleActions.onQuery.js │ │ │ └── googleActions.onSync.js │ │ ├── traits │ │ │ ├── googleActions.brightness.trait.js │ │ │ ├── googleActions.colorSetting.trait.js │ │ │ ├── googleActions.onOff.trait.js │ │ │ ├── googleActions.openClose.trait.js │ │ │ └── index.js │ │ └── utils │ │ │ ├── googleActions.determineTrait.js │ │ │ ├── googleActions.determineTypeAndTraits.js │ │ │ ├── googleActions.queryDeviceConverter.js │ │ │ └── googleActions.syncDeviceConverter.js │ ├── package-lock.json │ └── package.json ├── google-cast │ ├── api │ │ └── google_cast.controller.js │ ├── index.js │ ├── lib │ │ ├── google_cast.init.js │ │ ├── google_cast.scan.js │ │ ├── google_cast.setValue.js │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ └── convertToGladysDevice.js ├── homekit │ ├── api │ │ └── homekit.controller.js │ ├── index.js │ ├── lib │ │ ├── buildAccessory.js │ │ ├── buildService.js │ │ ├── createBridge.js │ │ ├── deviceMappings.js │ │ ├── index.js │ │ ├── newPinCode.js │ │ ├── newUsername.js │ │ ├── notifyChange.js │ │ ├── resetBridge.js │ │ └── sendState.js │ ├── package-lock.json │ └── package.json ├── index.js ├── lan-manager │ ├── api │ │ └── lan-manager.controller.js │ ├── index.js │ ├── lib │ │ ├── index.js │ │ ├── lan-manager.constants.js │ │ ├── lan-manager.getConfiguration.js │ │ ├── lan-manager.getDiscoveredDevices.js │ │ ├── lan-manager.getStatus.js │ │ ├── lan-manager.init.js │ │ ├── lan-manager.initPresenceScanner.js │ │ ├── lan-manager.loadConfiguration.js │ │ ├── lan-manager.mergeWithExistingDevice.js │ │ ├── lan-manager.saveConfiguration.js │ │ ├── lan-manager.scan.js │ │ ├── lan-manager.scanPresence.js │ │ ├── lan-manager.stop.js │ │ └── lan-manager.transformDevice.js │ ├── package-lock.json │ └── package.json ├── matter │ ├── api │ │ └── matter.controller.js │ ├── index.js │ ├── lib │ │ ├── index.js │ │ ├── matter.backupController.js │ │ ├── matter.checkIpv6.js │ │ ├── matter.decommission.js │ │ ├── matter.getDevices.js │ │ ├── matter.getNodes.js │ │ ├── matter.handleNode.js │ │ ├── matter.init.js │ │ ├── matter.listenToStateChange.js │ │ ├── matter.pairDevice.js │ │ ├── matter.refreshDevices.js │ │ ├── matter.restoreBackup.js │ │ ├── matter.setValue.js │ │ └── matter.stop.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ ├── constants.js │ │ └── convertToGladysDevice.js ├── melcloud │ ├── api │ │ └── melcloud.controller.js │ ├── index.js │ ├── lib │ │ ├── device │ │ │ ├── air-to-air.device.js │ │ │ └── melcloud.convertDevice.js │ │ ├── index.js │ │ ├── melcloud.connect.js │ │ ├── melcloud.disconnect.js │ │ ├── melcloud.discoverDevices.js │ │ ├── melcloud.getConfiguration.js │ │ ├── melcloud.init.js │ │ ├── melcloud.loadDevices.js │ │ ├── melcloud.poll.js │ │ ├── melcloud.saveConfiguration.js │ │ ├── melcloud.setValue.js │ │ └── utils │ │ │ └── melcloud.constants.js │ ├── package-lock.json │ └── package.json ├── mqtt │ ├── api │ │ └── mqtt.controller.js │ ├── docker │ │ └── eclipse-mosquitto-container.json │ ├── index.js │ ├── lib │ │ ├── checkDockerNetwork.js │ │ ├── configureContainer.js │ │ ├── connect.js │ │ ├── constants.js │ │ ├── disconnect.js │ │ ├── getConfiguration.js │ │ ├── handleNewMessage.js │ │ ├── handler │ │ │ ├── handleDeviceCustomTopicMessage.js │ │ │ └── handleGladysMessage.js │ │ ├── index.js │ │ ├── init.js │ │ ├── installContainer.js │ │ ├── listenToCustomMqttTopicIfNeeded.js │ │ ├── publish.js │ │ ├── saveConfiguration.js │ │ ├── setDebugMode.js │ │ ├── setValue.js │ │ ├── status.js │ │ ├── subscribe.js │ │ ├── unListenToCustomMqttTopic.js │ │ ├── unsubscribe.js │ │ └── updateContainer.js │ ├── package-lock.json │ └── package.json ├── netatmo │ ├── .eslintrc.json │ ├── api │ │ └── netatmo.controller.js │ ├── index.js │ ├── lib │ │ ├── device │ │ │ ├── netatmo.buildFeaturesCommon.js │ │ │ ├── netatmo.buildFeaturesCommonTemp.js │ │ │ ├── netatmo.buildFeaturesSpecifEnergy.js │ │ │ ├── netatmo.buildFeaturesSpecifWeather.js │ │ │ ├── netatmo.convertDeviceEnergy.js │ │ │ ├── netatmo.convertDeviceNotSupported.js │ │ │ ├── netatmo.convertDeviceWeather.js │ │ │ ├── netatmo.deviceMapping.js │ │ │ └── netatmo.updateNRV.js │ │ ├── index.js │ │ ├── netatmo.connect.js │ │ ├── netatmo.disconnect.js │ │ ├── netatmo.discoverDevices.js │ │ ├── netatmo.getAccessToken.js │ │ ├── netatmo.getConfiguration.js │ │ ├── netatmo.getRefreshToken.js │ │ ├── netatmo.getStatus.js │ │ ├── netatmo.init.js │ │ ├── netatmo.loadDeviceDetails.js │ │ ├── netatmo.loadDevices.js │ │ ├── netatmo.loadThermostatDetails.js │ │ ├── netatmo.loadWeatherStationDetails.js │ │ ├── netatmo.pollRefreshingToken.js │ │ ├── netatmo.pollRefreshingValues.js │ │ ├── netatmo.refreshingTokens.js │ │ ├── netatmo.retrieveTokens.js │ │ ├── netatmo.saveConfiguration.js │ │ ├── netatmo.saveStatus.js │ │ ├── netatmo.setTokens.js │ │ ├── netatmo.setValue.js │ │ ├── netatmo.updateValues.js │ │ ├── update │ │ │ ├── netatmo.updateNAMain.js │ │ │ ├── netatmo.updateNAModule1.js │ │ │ ├── netatmo.updateNAModule2.js │ │ │ ├── netatmo.updateNAModule3.js │ │ │ ├── netatmo.updateNAModule4.js │ │ │ ├── netatmo.updateNAPlug.js │ │ │ ├── netatmo.updateNATherm1.js │ │ │ └── netatmo.updateNRV.js │ │ └── utils │ │ │ ├── netatmo.buildScopesConfig.js │ │ │ └── netatmo.constants.js │ ├── package-lock.json │ └── package.json ├── nextcloud-talk │ ├── index.js │ ├── lib │ │ ├── bot │ │ │ ├── bot.poll.js │ │ │ ├── bot.startPolling.js │ │ │ └── bot.stopPolling.js │ │ ├── index.js │ │ └── message │ │ │ ├── message.connect.js │ │ │ ├── message.disconnect.js │ │ │ ├── message.new.js │ │ │ └── message.send.js │ ├── package-lock.json │ └── package.json ├── node-red │ ├── api │ │ └── node-red.controller.js │ ├── docker │ │ ├── gladys-node-red-container.json │ │ └── settings.txt │ ├── index.js │ ├── lib │ │ ├── checkForContainerUpdates.js │ │ ├── configureContainer.js │ │ ├── constants.js │ │ ├── disconnect.js │ │ ├── getConfiguration.js │ │ ├── index.js │ │ ├── init.js │ │ ├── installContainer.js │ │ ├── isEnabled.js │ │ ├── saveConfiguration.js │ │ └── status.js │ ├── package-lock.json │ └── package.json ├── openweather │ ├── index.js │ ├── lib │ │ └── formatResults.js │ ├── package-lock.json │ └── package.json ├── philips-hue │ ├── api │ │ └── hue.controller.js │ ├── index.js │ ├── lib │ │ ├── light │ │ │ ├── index.js │ │ │ ├── light.activateScene.js │ │ │ ├── light.configureBridge.js │ │ │ ├── light.getBridges.js │ │ │ ├── light.getLights.js │ │ │ ├── light.getScenes.js │ │ │ ├── light.init.js │ │ │ ├── light.poll.js │ │ │ ├── light.setValue.js │ │ │ └── light.syncWithBridge.js │ │ ├── models │ │ │ ├── color.js │ │ │ ├── colorWithTemperature.js │ │ │ ├── plugOnOff.js │ │ │ ├── white.js │ │ │ └── whiteWithTemperature.js │ │ └── utils │ │ │ ├── consts.js │ │ │ └── parseExternalId.js │ ├── package-lock.json │ └── package.json ├── rtsp-camera │ ├── api │ │ └── rtspCamera.controller.js │ ├── index.js │ ├── lib │ │ ├── checkIfLiveActive.js │ │ ├── convertLocalStreamToGateway.js │ │ ├── getImage.js │ │ ├── index.js │ │ ├── liveActivePing.js │ │ ├── onNewCameraFile.js │ │ ├── poll.js │ │ ├── sendCameraFileToGateway.js │ │ ├── startStreaming.js │ │ ├── startStreamingIfNotStarted.js │ │ └── stopStreaming.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ └── validateStreamParams.js ├── sonos │ ├── api │ │ └── sonos.controller.js │ ├── index.js │ ├── lib │ │ ├── index.js │ │ ├── sonos.init.js │ │ ├── sonos.onAvTransportEvent.js │ │ ├── sonos.onVolumeEvent.js │ │ ├── sonos.scan.js │ │ └── sonos.setValue.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ └── convertToGladysDevice.js ├── tasmota │ ├── api │ │ └── tasmota.controller.js │ ├── index.js │ ├── lib │ │ ├── features │ │ │ ├── colorChannel.js │ │ │ ├── colorScheme.js │ │ │ ├── colorSpeed.js │ │ │ ├── colorTemperature.js │ │ │ ├── counter.js │ │ │ ├── device_temperature.js │ │ │ ├── dimmer.js │ │ │ ├── distance.js │ │ │ ├── energy.apparentPower.js │ │ │ ├── energy.current.js │ │ │ ├── energy.power.js │ │ │ ├── energy.reactivePower.js │ │ │ ├── energy.today.js │ │ │ ├── energy.total.js │ │ │ ├── energy.voltage.js │ │ │ ├── energy.yesterday.js │ │ │ ├── humidity.js │ │ │ ├── index.js │ │ │ ├── modules.js │ │ │ ├── power.js │ │ │ └── temperature.js │ │ ├── http │ │ │ ├── index.js │ │ │ ├── tasmota.http.connect.js │ │ │ ├── tasmota.http.constants.js │ │ │ ├── tasmota.http.disconnect.js │ │ │ ├── tasmota.http.getDiscoveredDevices.js │ │ │ ├── tasmota.http.getValue.js │ │ │ ├── tasmota.http.request.js │ │ │ ├── tasmota.http.scan.js │ │ │ ├── tasmota.http.setValue.js │ │ │ ├── tasmota.http.status.js │ │ │ └── tasmota.http.subStatus.js │ │ ├── index.js │ │ ├── mqtt │ │ │ ├── index.js │ │ │ ├── tasmota.mqtt.connect.js │ │ │ ├── tasmota.mqtt.disconnect.js │ │ │ ├── tasmota.mqtt.getDiscoveredDevices.js │ │ │ ├── tasmota.mqtt.handleMessage.js │ │ │ ├── tasmota.mqtt.scan.js │ │ │ ├── tasmota.mqtt.setValue.js │ │ │ ├── tasmota.mqtt.status.js │ │ │ └── tasmota.mqtt.subStatus.js │ │ ├── tasmota.connect.js │ │ ├── tasmota.constants.js │ │ ├── tasmota.disconnect.js │ │ ├── tasmota.getDiscoveredDevices.js │ │ ├── tasmota.getHandler.js │ │ ├── tasmota.getProtocolFromDevice.js │ │ ├── tasmota.mergeWithExistingDevice.js │ │ ├── tasmota.notifyNewDevice.js │ │ ├── tasmota.poll.js │ │ ├── tasmota.scan.js │ │ ├── tasmota.setValue.js │ │ └── utils │ │ │ └── tasmota.featureStatus.js │ ├── package-lock.json │ └── package.json ├── telegram │ ├── api │ │ └── telegram.controller.js │ ├── index.js │ ├── lib │ │ ├── index.js │ │ ├── message.connect.js │ │ ├── message.disconnect.js │ │ ├── message.getCustomLink.js │ │ ├── message.linkUser.js │ │ ├── message.new.js │ │ └── message.send.js │ ├── package-lock.json │ └── package.json ├── tp-link │ ├── api │ │ └── tp-link.controller.js │ ├── index.js │ ├── lib │ │ ├── models │ │ │ ├── bulb.js │ │ │ ├── device.js │ │ │ └── plug.js │ │ ├── smart-device │ │ │ ├── index.js │ │ │ ├── smart-device.getDevices.js │ │ │ ├── smart-device.poll.js │ │ │ └── smart-device.setValue.js │ │ └── utils │ │ │ ├── consts.js │ │ │ └── parseExternalId.js │ ├── package-lock.json │ └── package.json ├── tuya │ ├── api │ │ └── tuya.controller.js │ ├── index.js │ ├── lib │ │ ├── device │ │ │ ├── tuya.convertDevice.js │ │ │ ├── tuya.convertFeature.js │ │ │ ├── tuya.convertUnit.js │ │ │ └── tuya.deviceMapping.js │ │ ├── index.js │ │ ├── tuya.connect.js │ │ ├── tuya.disconnect.js │ │ ├── tuya.discoverDevices.js │ │ ├── tuya.getAccessToken.js │ │ ├── tuya.getConfiguration.js │ │ ├── tuya.getRefreshToken.js │ │ ├── tuya.init.js │ │ ├── tuya.loadDeviceDetails.js │ │ ├── tuya.loadDevices.js │ │ ├── tuya.poll.js │ │ ├── tuya.saveConfiguration.js │ │ ├── tuya.setTokens.js │ │ ├── tuya.setValue.js │ │ └── utils │ │ │ └── tuya.constants.js │ ├── package-lock.json │ └── package.json ├── usb │ ├── api │ │ └── usb.controller.js │ ├── index.js │ ├── package-lock.json │ └── package.json ├── xiaomi │ ├── api │ │ └── xiaomi.controller.js │ ├── index.js │ ├── lib │ │ ├── commands │ │ │ ├── xiaomi.getSensors.js │ │ │ ├── xiaomi.listen.js │ │ │ └── xiaomi.setValue.js │ │ ├── event │ │ │ ├── xiaomi.addDevice.js │ │ │ ├── xiaomi.listening.js │ │ │ ├── xiaomi.newValueCube.js │ │ │ ├── xiaomi.newValueDuplexWiredSwitch.js │ │ │ ├── xiaomi.newValueDuplexWiredSwitchNeutral.js │ │ │ ├── xiaomi.newValueDuplexWirelessSwitch.js │ │ │ ├── xiaomi.newValueGateway.js │ │ │ ├── xiaomi.newValueLeak.js │ │ │ ├── xiaomi.newValueMagnetSensor.js │ │ │ ├── xiaomi.newValueMotionSensor.js │ │ │ ├── xiaomi.newValuePlug.js │ │ │ ├── xiaomi.newValueSingleWiredSwitch.js │ │ │ ├── xiaomi.newValueSingleWiredSwitchNeutral.js │ │ │ ├── xiaomi.newValueSingleWirelessSwitch.js │ │ │ ├── xiaomi.newValueSmoke.js │ │ │ ├── xiaomi.newValueSwitch.js │ │ │ ├── xiaomi.newValueTemperatureSensor.js │ │ │ ├── xiaomi.newValueVibration.js │ │ │ └── xiaomi.onMessage.js │ │ ├── index.js │ │ └── utils │ │ │ ├── deviceStatus.js │ │ │ ├── generateGatewayKey.js │ │ │ └── getBatteryPercent.js │ ├── package-lock.json │ └── package.json ├── zigbee2mqtt │ ├── adapters │ │ └── index.js │ ├── api │ │ └── zigbee2mqtt.controller.js │ ├── docker │ │ ├── gladys-z2m-mqtt-container.json │ │ ├── gladys-z2m-zigbee2mqtt-container.json │ │ └── mosquitto.conf │ ├── exposes │ │ ├── binaryType.js │ │ ├── compositeType.js │ │ ├── enumType.js │ │ ├── index.js │ │ └── numericType.js │ ├── index.js │ ├── lib │ │ ├── backup.js │ │ ├── checkForContainerUpdates.js │ │ ├── configureContainer.js │ │ ├── connect.js │ │ ├── constants.js │ │ ├── disconnect.js │ │ ├── events │ │ │ └── emitStatusEvent.js │ │ ├── findMatchingExpose.js │ │ ├── getConfiguration.js │ │ ├── getDiscoveredDevices.js │ │ ├── getManagedAdapters.js │ │ ├── getPermitJoin.js │ │ ├── getSetup.js │ │ ├── handleMqttMessage.js │ │ ├── index.js │ │ ├── init.js │ │ ├── installMqttContainer.js │ │ ├── installZ2mContainer.js │ │ ├── isEnabled.js │ │ ├── publish.js │ │ ├── readValue.js │ │ ├── restoreZ2mBackup.js │ │ ├── saveConfiguration.js │ │ ├── saveOrDestroyVariable.js │ │ ├── saveZ2mBackup.js │ │ ├── setPermitJoin.js │ │ ├── setValue.js │ │ ├── setup.js │ │ ├── status.js │ │ └── subscribe.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ ├── convertDevice.js │ │ ├── convertFeature.js │ │ └── features │ │ ├── buildFeatures.js │ │ ├── completeFeature.js │ │ ├── mapDefinition.js │ │ ├── mapExpose.js │ │ └── mapUnit.js └── zwavejs-ui │ ├── api │ └── zwaveJSUI.controller.js │ ├── index.js │ ├── lib │ ├── constants.js │ ├── index.js │ ├── zwaveJSUI.connect.js │ ├── zwaveJSUI.disconnect.js │ ├── zwaveJSUI.getConfiguration.js │ ├── zwaveJSUI.getDevice.js │ ├── zwaveJSUI.getZwaveJsDevice.js │ ├── zwaveJSUI.handleNewMessage.js │ ├── zwaveJSUI.init.js │ ├── zwaveJSUI.onNewDeviceDiscover.js │ ├── zwaveJSUI.onNodeValueUpdated.js │ ├── zwaveJSUI.publish.js │ ├── zwaveJSUI.saveConfiguration.js │ ├── zwaveJSUI.scan.js │ └── zwaveJSUI.setValue.js │ ├── package-lock.json │ ├── package.json │ └── utils │ ├── cleanNames.js │ ├── convertToGladysDevice.js │ ├── getDeviceFeatureName.js │ ├── getProperty.js │ └── refineCategory.js ├── test ├── benchmark │ ├── scenes.js │ └── triggers.js ├── bootstrap.test.js ├── controllers │ ├── area │ │ └── area.controller.test.js │ ├── calendar │ │ └── calendar.test.js │ ├── camera │ │ └── camera.controller.test.js │ ├── dashboard │ │ └── dashboard.controller.test.js │ ├── device │ │ └── device.controller.test.js │ ├── gateway │ │ ├── gateway.controller.test.js │ │ └── srpFixture.json │ ├── gatway.test.js │ ├── house │ │ ├── house.alarm.test.js │ │ └── house.test.js │ ├── http │ │ └── http.test.js │ ├── job │ │ └── job.test.js │ ├── light │ │ └── light.turnOn.test.js │ ├── location │ │ └── location.test.js │ ├── message │ │ └── message.test.js │ ├── notFound.test.js │ ├── ping │ │ └── ping.controller.test.js │ ├── request.test.js │ ├── room │ │ └── room.test.js │ ├── scene │ │ └── scene.test.js │ ├── service │ │ └── service.controller.test.js │ ├── session │ │ └── session.test.js │ ├── system │ │ └── system.controller.test.js │ ├── user │ │ ├── user.create.test.js │ │ ├── user.delete.test.js │ │ ├── user.forgotPassword.test.js │ │ ├── user.get.test.js │ │ ├── user.getAccessToken.test.js │ │ ├── user.getBySelector.test.js │ │ ├── user.getMySelf.test.js │ │ ├── user.getPicture.test.js │ │ ├── user.getSetupState.test.js │ │ ├── user.login.test.js │ │ ├── user.resetPassword.test.js │ │ ├── user.update.test.js │ │ └── user.updateMySelf.test.js │ ├── variable │ │ └── variable.test.js │ └── weather │ │ └── weather.test.js ├── helpers │ └── db.test.js ├── lib │ ├── area │ │ ├── area.checkNewLocation.test.js │ │ └── area.test.js │ ├── brain │ │ ├── brain.e2e.test.js │ │ └── brain.test.js │ ├── calendar │ │ ├── calendar.event.test.js │ │ └── calendar.test.js │ ├── dashboard │ │ ├── dashboard.create.test.js │ │ ├── dashboard.destroy.test.js │ │ ├── dashboard.get.test.js │ │ ├── dashboard.getBySelector.test.js │ │ ├── dashboard.update.test.js │ │ └── dashboard.updateOrder.test.js │ ├── device │ │ ├── camera │ │ │ ├── camera.command.test.js │ │ │ ├── camera.get.test.js │ │ │ ├── camera.getImage.test.js │ │ │ ├── camera.getImageInRoom.test.js │ │ │ ├── camera.getLiveImage.test.js │ │ │ └── camera.setImage.test.js │ │ ├── device.addFeature.test.js │ │ ├── device.addParam.test.js │ │ ├── device.checkBatteries.test.js │ │ ├── device.create.test.js │ │ ├── device.destroy.test.js │ │ ├── device.get.test.js │ │ ├── device.getBySelector.test.js │ │ ├── device.getDeviceFeaturesAggregates.test.js │ │ ├── device.getDeviceFeaturesAggregatesMulti.test.js │ │ ├── device.getDuckDbMigrationState.test.js │ │ ├── device.init.test.js │ │ ├── device.migrateFromSQLiteToDuckDb.test.js │ │ ├── device.newStateEvent.test.js │ │ ├── device.notify.test.js │ │ ├── device.onPurgeStatesEvent.test.js │ │ ├── device.poll.test.js │ │ ├── device.purgeAllSqliteStates.test.js │ │ ├── device.purgeStates.test.js │ │ ├── device.purgeStatesByFeatureId.test.js │ │ ├── device.saveHistoricalState.test.js │ │ ├── device.saveState.test.js │ │ ├── device.setValue.test.js │ │ ├── device.setupPoll.test.js │ │ ├── humidity-sensor │ │ │ └── humidity-sensor.test.js │ │ ├── light │ │ │ ├── light.buildLightObject.test.js │ │ │ ├── light.command.test.js │ │ │ ├── light.init.test.js │ │ │ ├── light.turnOff.test.js │ │ │ └── light.turnOn.test.js │ │ ├── switch │ │ │ └── switch.command.test.js │ │ └── temperature-sensor │ │ │ └── temperature-sensor.test.js │ ├── event │ │ └── event.test.js │ ├── gateway │ │ ├── GladysGatewayClientMock.test.js │ │ ├── encoded-gladys-db-and-duckdb-backup.tar.gz.enc │ │ ├── encoded-old-gladys-db-backup.db.gz.enc │ │ ├── gateway.backup.test.js │ │ ├── gateway.checkIfBackupNeeded.test.js │ │ ├── gateway.disconnect.test.js │ │ ├── gateway.downloadBackup.test.js │ │ ├── gateway.enedis.test.js │ │ ├── gateway.forwardDeviceStateToAlexa.test.js │ │ ├── gateway.forwardDeviceStateToGoogleHome.test.js │ │ ├── gateway.forwardMessageToOpenAI.test.js │ │ ├── gateway.forwardWebsockets.test.js │ │ ├── gateway.getBackups.test.js │ │ ├── gateway.getEcowattSignals.test.js │ │ ├── gateway.getEdfTempo.test.js │ │ ├── gateway.getLatestGladysVersion.test.js │ │ ├── gateway.getTTSApiUrl.test.js │ │ ├── gateway.getUsersKeys.test.js │ │ ├── gateway.handleAlexaMessage.test.js │ │ ├── gateway.handleNewMessage.test.js │ │ ├── gateway.init.test.js │ │ ├── gateway.login.test.js │ │ ├── gateway.openAi.test.js │ │ ├── gateway.restoreBackup.test.js │ │ ├── gateway.restoreBackupEvent.test.js │ │ ├── gladys_backup_parquet_folder │ │ │ ├── load.sql │ │ │ ├── schema.sql │ │ │ └── t_device_feature_state.parquet │ │ ├── real-gladys-db-backup.db.gz.dbfile │ │ ├── this_db_has_no_users.dbfile │ │ ├── this_file_has_no_user_table.dbfile │ │ └── this_file_is_not_a_valid_db.dbfile │ ├── house │ │ ├── house.arm.test.js │ │ ├── house.disarm.test.js │ │ ├── house.disarmWithCode.test.js │ │ ├── house.panic.test.js │ │ ├── house.partialArm.test.js │ │ └── house.test.js │ ├── http │ │ ├── AxiosMock.test.js │ │ └── http.test.js │ ├── job │ │ └── job.test.js │ ├── location │ │ └── location.test.js │ ├── message │ │ ├── message.create.test.js │ │ ├── message.get.test.js │ │ ├── message.handleMessage.test.js │ │ ├── message.purge.test.js │ │ ├── message.reply.test.js │ │ ├── message.replyByIntent.test.js │ │ └── message.sendToUser.test.js │ ├── room │ │ └── room.test.js │ ├── scene │ │ ├── actions │ │ │ ├── scene.action.askAi.test.js │ │ │ ├── scene.action.blinkDevices.test.js │ │ │ ├── scene.action.checkAlarmMode.test.js │ │ │ ├── scene.action.checkTime.test.js │ │ │ ├── scene.action.conditionIfThenElse.test.js │ │ │ ├── scene.action.continueOnlyIf.test.js │ │ │ ├── scene.action.delay.test.js │ │ │ ├── scene.action.ecowattCondition.test.js │ │ │ ├── scene.action.edfTempoCondition.test.js │ │ │ ├── scene.action.getValue.test.js │ │ │ ├── scene.action.httpRequest.test.js │ │ │ ├── scene.action.isEventRunnning.test.js │ │ │ ├── scene.action.playNotification.test.js │ │ │ ├── scene.action.sendCameraMessage.test.js │ │ │ ├── scene.action.sendMessage.test.js │ │ │ ├── scene.action.sendMqttMessage.test.js │ │ │ ├── scene.action.sendSms.test.js │ │ │ ├── scene.action.sendZigbee2MqttMessage.test.js │ │ │ └── scene.action.setAlarmMode.test.js │ │ ├── scene.addScene.test.js │ │ ├── scene.cancelTriggers.test.js │ │ ├── scene.checkCalendarTriggers.test.js │ │ ├── scene.checkTrigger.test.js │ │ ├── scene.command.test.js │ │ ├── scene.create.test.js │ │ ├── scene.dailyUpdate.test.js │ │ ├── scene.destroy.test.js │ │ ├── scene.duplicate.test.js │ │ ├── scene.execute.test.js │ │ ├── scene.executeActions.test.js │ │ ├── scene.executeSingleAction.test.js │ │ ├── scene.get.test.js │ │ ├── scene.getBySelector.test.js │ │ ├── scene.getTag.test.js │ │ ├── scene.init.test.js │ │ ├── scene.update.test.js │ │ └── triggers │ │ │ ├── scene.trigger.alarmMode.test.js │ │ │ ├── scene.trigger.deviceNewState.test.js │ │ │ └── scene.trigger.mqttReceived.test.js │ ├── scheduler │ │ ├── scheduler.cancelJob.test.js │ │ ├── scheduler.init.test.js │ │ └── scheduler.scheduleJob.test.js │ ├── service │ │ ├── service.getLocalServiceByName.test.js │ │ ├── service.getUsage.test.js │ │ ├── service.load.test.js │ │ ├── service.start.test.js │ │ ├── service.startAll.test.js │ │ ├── service.stop.test.js │ │ └── service.test.js │ ├── session │ │ └── session.test.js │ ├── state │ │ └── state.test.js │ ├── system │ │ ├── DockerApiMock.test.js │ │ ├── DockerodeMock.test.js │ │ ├── system.checkIfGladysUpgraded.test.js │ │ ├── system.createContainer.test.js │ │ ├── system.exec.test.js │ │ ├── system.getContainerMounts.test.js │ │ ├── system.getContainers.test.js │ │ ├── system.getDiskSpace.test.js │ │ ├── system.getGladysBasePath.test.js │ │ ├── system.getGladysContainerId.test.js │ │ ├── system.getInfos.test.js │ │ ├── system.getNetworkMode.test.js │ │ ├── system.init.test.js │ │ ├── system.inspectContainer.test.js │ │ ├── system.installUpgrade.test.js │ │ ├── system.isDocker.test.js │ │ ├── system.pull.test.js │ │ ├── system.removeContainer.test.js │ │ ├── system.restartContainer.test.js │ │ ├── system.shutdown.test.js │ │ ├── system.stopContainer.test.js │ │ └── system.vacuum.test.js │ ├── user │ │ ├── tooLongImage.json │ │ ├── user.create.test.js │ │ ├── user.destroy.test.js │ │ ├── user.forgotPassword.test.js │ │ ├── user.get.test.js │ │ ├── user.getById.test.js │ │ ├── user.getByRole.test.js │ │ ├── user.getBySelector.test.js │ │ ├── user.getByTelegramUserId.test.js │ │ ├── user.getPicture.test.js │ │ ├── user.getUserCount.test.js │ │ ├── user.update.test.js │ │ ├── user.updateBySelector.test.js │ │ └── user.updatePassord.test.js │ ├── variable │ │ ├── variable.destroy.test.js │ │ ├── variable.getValue.test.js │ │ ├── variable.getVariables.test.js │ │ └── variable.setValue.test.js │ └── weather │ │ ├── weather.command.test.js │ │ └── weather.get.test.js ├── middlewares │ ├── authMiddleware.test.js │ ├── corsMiddleware.test.js │ └── errorMiddleware.test.js ├── security │ └── user.test.js ├── services │ ├── airplay │ │ ├── api │ │ │ └── airplay.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── airplay.init.test.js │ │ │ ├── airplay.setValue.test.js │ │ │ └── airplay_scan.test.js │ ├── alexa │ │ └── lib │ │ │ ├── alexa.onDiscovery.test.js │ │ │ ├── alexa.onExecute.test.js │ │ │ └── alexa.onReportState.test.js │ ├── bluetooth │ │ ├── BluetoothMock.test.js │ │ ├── api │ │ │ └── bluetooth.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── commands │ │ │ ├── bluetooth.applyOnPeripheral.test.js │ │ │ ├── bluetooth.completeDevice.test.js │ │ │ ├── bluetooth.getDiscoveredDevice.test.js │ │ │ ├── bluetooth.getDiscoveredDevices.test.js │ │ │ ├── bluetooth.getStatus.test.js │ │ │ ├── bluetooth.readDevice.test.js │ │ │ ├── bluetooth.scan.test.js │ │ │ ├── bluetooth.scanDevice.test.js │ │ │ ├── bluetooth.scanPresence.test.js │ │ │ ├── bluetooth.start.test.js │ │ │ ├── bluetooth.stop.test.js │ │ │ ├── bluetooth.stopScanPresence.test.js │ │ │ ├── bluetooth.subscribeDevice.test.js │ │ │ ├── bluetooth.unsubscribeDevice.test.js │ │ │ └── bluetooth.writeDevice.test.js │ │ │ ├── config │ │ │ ├── bluetooth.getConfiguration.test.js │ │ │ ├── bluetooth.initPresenceScanner.test.js │ │ │ └── bluetooth.saveConfiguration.test.js │ │ │ ├── device │ │ │ ├── bluetooth.information.test.js │ │ │ └── bluetooth.transformToDevice.test.js │ │ │ ├── events │ │ │ ├── bluetooth.discover.test.js │ │ │ ├── bluetooth.scanStart.test.js │ │ │ ├── bluetooth.scanStop.test.js │ │ │ └── bluetooth.stateChange.test.js │ │ │ └── utils │ │ │ ├── bluetooth.connect.test.js │ │ │ ├── bluetooth.discoverCharacteristics.test.js │ │ │ ├── bluetooth.discoverServices.test.js │ │ │ ├── bluetooth.subscribe.test.js │ │ │ └── bluetooth.unsubscribe.test.js │ ├── broadlink │ │ ├── api │ │ │ └── broadlink.controler.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── commands │ │ │ ├── broadlink.addPeripheral.test.js │ │ │ ├── broadlink.buildPeripheral.test.js │ │ │ ├── broadlink.getDevice.test.js │ │ │ ├── broadlink.getPeripherals.test.js │ │ │ ├── broadlink.init.test.js │ │ │ ├── broadlink.loadMapper.test.js │ │ │ ├── broadlink.poll.test.js │ │ │ ├── broadlink.setValue.test.js │ │ │ ├── broadlink.stop.test.js │ │ │ └── features │ │ │ │ ├── broadlink.light.test.js │ │ │ │ ├── broadlink.remote.test.js │ │ │ │ ├── broadlink.sensor.test.js │ │ │ │ └── broadlink.switch.test.js │ │ │ └── learn │ │ │ ├── broadlink.cancelLearn.test.js │ │ │ ├── broadlink.checkData.test.js │ │ │ ├── broadlink.learn.test.js │ │ │ └── broadlink.send.test.js │ ├── caldav │ │ ├── controllers │ │ │ └── caldav.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── calendar │ │ │ ├── cleanUp.test.js │ │ │ ├── disableCalendar.test.js │ │ │ ├── enableCalendar.test.js │ │ │ ├── formaters.test.js │ │ │ ├── requests.test.js │ │ │ ├── syncUserCalendars.test.js │ │ │ └── syncUserWebcals.test.js │ │ │ └── config │ │ │ └── index.test.js │ ├── callmebot │ │ ├── index.test.js │ │ └── lib │ │ │ └── messageHandler.test.js │ ├── checks.test.js │ ├── ecowatt │ │ ├── ecowatt.controller.test.js │ │ ├── ecowatt.data.js │ │ └── index.test.js │ ├── edf-tempo │ │ ├── edf-tempo.controller.test.js │ │ └── index.test.js │ ├── enedis │ │ ├── enedis.controller.test.js │ │ ├── enedis.sync.test.js │ │ └── index.test.js │ ├── ewelink │ │ ├── controllers │ │ │ └── ewelink.controller.test.js │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── device │ │ │ │ ├── connect.test.js │ │ │ │ ├── discover.test.js │ │ │ │ ├── poll.test.js │ │ │ │ ├── setValue.test.js │ │ │ │ └── throwErrorIfNeeded.test.js │ │ │ └── features │ │ │ │ └── features.test.js │ │ └── mocks │ │ │ ├── Gladys-2ch.json │ │ │ ├── Gladys-Basic.json │ │ │ ├── Gladys-offline.json │ │ │ ├── Gladys-pow.json │ │ │ ├── Gladys-th.json │ │ │ ├── Gladys-unhandled.json │ │ │ ├── consts.test.js │ │ │ ├── eweLink-2ch.json │ │ │ ├── eweLink-basic.json │ │ │ ├── eweLink-offline.json │ │ │ ├── eweLink-pow.json │ │ │ ├── eweLink-th.json │ │ │ ├── eweLink-unhandled.json │ │ │ ├── ewelink-api-empty.mock.test.js │ │ │ └── ewelink-api.mock.test.js │ ├── example │ │ ├── index.test.js │ │ ├── lib │ │ │ └── setValue.test.js │ │ └── mocks.test.js │ ├── free-mobile │ │ └── index.test.js │ ├── google-actions │ │ ├── index.test.js │ │ └── lib │ │ │ └── smarthome │ │ │ ├── devices_and_traits │ │ │ ├── googleActions.brightness.trait.light.test.js │ │ │ ├── googleActions.brightness.trait.switch.test.js │ │ │ ├── googleActions.colorSetting.trait.color.test.js │ │ │ ├── googleActions.colorSetting.trait.colorTemp.test.js │ │ │ ├── googleActions.openCloseTrait.trait.curtain.test.js │ │ │ └── googleActions.openCloseTrait.trait.shutter.test.js │ │ │ ├── googleActions.onExecute.test.js │ │ │ ├── googleActions.onQuery.test.js │ │ │ └── googleActions.onSync.test.js │ ├── google-cast │ │ ├── api │ │ │ └── google_cast.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── google_cast.init.test.js │ │ │ ├── google_cast.setValue.test.js │ │ │ └── google_cast_scan.test.js │ ├── homekit │ │ ├── controllers │ │ │ └── homekit.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── buildAccessory.test.js │ │ │ ├── buildService.test.js │ │ │ ├── createBridge.test.js │ │ │ ├── notifyChange.test.js │ │ │ ├── resetBridge.test.js │ │ │ └── sendState.test.js │ ├── lan-manager │ │ ├── api │ │ │ └── lan-manager.controler.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── lan-manager.getConfiguration.test.js │ │ │ ├── lan-manager.getDiscoveredDevices.test.js │ │ │ ├── lan-manager.initPresenceScanner.test.js │ │ │ ├── lan-manager.loadConfiguration.test.js │ │ │ ├── lan-manager.saveConfiguration.test.js │ │ │ ├── lan-manager.scan.test.js │ │ │ ├── lan-manager.scanPresence.test.js │ │ │ ├── lan-manager.stop.test.js │ │ │ └── lan-manager.transformDevice.test.js │ ├── matter │ │ ├── api │ │ │ └── matter.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── listenToStateChange.test.js │ │ │ ├── matter.backupController.test.js │ │ │ ├── matter.checkIpv6.test.js │ │ │ ├── matter.decommission.test.js │ │ │ ├── matter.getDevices.test.js │ │ │ ├── matter.getNodes.test.js │ │ │ ├── matter.init.test.js │ │ │ ├── matter.pairDevice.test.js │ │ │ ├── matter.refreshDevices.test.js │ │ │ ├── matter.restoreBackup.test.js │ │ │ ├── matter.setValue.test.js │ │ │ └── matter.stop.test.js │ ├── melcloud │ │ ├── index.test.js │ │ └── lib │ │ │ ├── controllers │ │ │ └── melcloud.controller.test.js │ │ │ ├── device │ │ │ └── feature │ │ │ │ ├── air-to-air.device.test.js │ │ │ │ └── melcloud.convertDevice.test.js │ │ │ ├── melcloud.connect.test.js │ │ │ ├── melcloud.disconnect.test.js │ │ │ ├── melcloud.discoverDevices.test.js │ │ │ ├── melcloud.getConfiguration.test.js │ │ │ ├── melcloud.init.test.js │ │ │ ├── melcloud.loadDevices.test.js │ │ │ ├── melcloud.poll.test.js │ │ │ ├── melcloud.saveConfiguration.test.js │ │ │ └── melcloud.setValue.test.js │ ├── mqtt │ │ ├── api │ │ │ └── mqtt.controler.test.js │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── configureContainer.test.js │ │ │ ├── connect.test.js │ │ │ ├── getConfiguration.test.js │ │ │ ├── handleNewMessage.test.js │ │ │ ├── init.test.js │ │ │ ├── installContainer.test.js │ │ │ ├── listenToCustomMqttTopicIfNeeded.test.js │ │ │ ├── publish.test.js │ │ │ ├── saveConfiguration.test.js │ │ │ ├── setDebugMode.test.js │ │ │ ├── setValue.test.js │ │ │ ├── status.test.js │ │ │ ├── unListenToCustomMqttTopic.test.js │ │ │ ├── unsubscribe.test.js │ │ │ └── updateContainer.test.js │ │ ├── mocks.test.js │ │ └── mqttHandler.test.js │ ├── netatmo │ │ ├── .eslintrc.json │ │ ├── controllers │ │ │ └── netatmo.controller.test.js │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── device │ │ │ │ ├── netatmo.convertDeviceEnergy.test.js │ │ │ │ ├── netatmo.convertDeviceNotSupported.test.js │ │ │ │ ├── netatmo.convertDeviceWeather.test.js │ │ │ │ └── netatmo.deviceMapping.test.js │ │ │ ├── index.test.js │ │ │ ├── netatmo.connect.test.js │ │ │ ├── netatmo.disconnect.test.js │ │ │ ├── netatmo.discoverDevices.test.js │ │ │ ├── netatmo.getAccessToken.test.js │ │ │ ├── netatmo.getConfiguration.test.js │ │ │ ├── netatmo.getRefreshToken.test.js │ │ │ ├── netatmo.getStatus.test.js │ │ │ ├── netatmo.init.test.js │ │ │ ├── netatmo.loadDeviceDetails.test.js │ │ │ ├── netatmo.loadDevices.test.js │ │ │ ├── netatmo.loadThermostatDetails.test.js │ │ │ ├── netatmo.loadWeatherStationDetails.test.js │ │ │ ├── netatmo.pollRefreshingTokens.test.js │ │ │ ├── netatmo.pollRefreshingValues.test.js │ │ │ ├── netatmo.refreshingTokens.test.js │ │ │ ├── netatmo.retrieveToken.test.js │ │ │ ├── netatmo.saveConfiguration.test.js │ │ │ ├── netatmo.saveStatus.test.js │ │ │ ├── netatmo.setTokens.test.js │ │ │ ├── netatmo.setValue.test.js │ │ │ ├── netatmo.updateValues.test.js │ │ │ └── update │ │ │ │ ├── netatmo.updateNAMain.test.js │ │ │ │ ├── netatmo.updateNAModule1.test.js │ │ │ │ ├── netatmo.updateNAModule2.test.js │ │ │ │ ├── netatmo.updateNAModule3.test.js │ │ │ │ ├── netatmo.updateNAModule4.test.js │ │ │ │ ├── netatmo.updateNAPlug.test.js │ │ │ │ ├── netatmo.updateNATherm1.test.js │ │ │ │ └── netatmo.updateNRV.test.js │ │ ├── netatmo.convertDevices.mock.test.json │ │ ├── netatmo.discoverDevices.mock.test.json │ │ ├── netatmo.getThermostat.mock.test.json │ │ ├── netatmo.getWeatherStation.mock.test.json │ │ ├── netatmo.homesdata.mock.test.json │ │ ├── netatmo.homestatus.mock.test.json │ │ ├── netatmo.loadDevices.mock.test.json │ │ ├── netatmo.loadDevicesComplete.mock.test.json │ │ ├── netatmo.loadDevicesDetails.mock.test.json │ │ ├── netatmo.loadThermostatDetails.mock.test.json │ │ ├── netatmo.loadWeatherStationDetails.mock.test.json │ │ └── netatmo.mock.test.js │ ├── nextcloud-talk │ │ ├── index.test.js │ │ └── lib │ │ │ └── messageHandler.test.js │ ├── node-red │ │ ├── api │ │ │ └── node-red.controller.test.js │ │ ├── expectedDefaultContent.txt │ │ ├── expectedNodeRedContent.txt │ │ ├── expectedOtherNodeRedContent.txt │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── checkForContainerUpdates.test.js │ │ │ ├── configureContainer.test.js │ │ │ ├── disconnect.test.js │ │ │ ├── getConfiguration.test.js │ │ │ ├── init.test.js │ │ │ ├── installContainer.test.js │ │ │ ├── isEnabled.test.js │ │ │ ├── saveConfiguration.test.js │ │ │ └── status.test.js │ │ └── mockPassword.js │ ├── openweather │ │ ├── expected-result.json │ │ ├── fakeOpenWeatherService.js │ │ ├── openweather.test.js │ │ ├── weather-data-new.json │ │ └── weather-forecast.json │ ├── philips-hue │ │ ├── controllers │ │ │ ├── activateScene.controller.test.js │ │ │ ├── configureBridge.controller.test.js │ │ │ ├── getBridges.controller.test.js │ │ │ ├── getLights.controller.test.js │ │ │ ├── getScenes.controller.test.js │ │ │ └── syncWithBridge.controller.test.js │ │ ├── index.test.js │ │ ├── light │ │ │ ├── light.configureBridge.test.js │ │ │ ├── light.getBridges.test.js │ │ │ ├── light.getLights.test.js │ │ │ ├── light.poll.test.js │ │ │ ├── light.setValue.test.js │ │ │ ├── light.syncWithBridge.test.js │ │ │ ├── lights.activateScene.test.js │ │ │ └── lights.getScenes.test.js │ │ ├── lights.json │ │ ├── mocks.test.js │ │ └── utils │ │ │ └── parseExternalId.test.js │ ├── rtsp-camera │ │ ├── controllers │ │ │ └── rtspCamera.controller.test.js │ │ ├── rstp.convertLocalStreamToGateway.test.js │ │ ├── rstpCamera.onNewCameraFile.test.js │ │ ├── rtspCamera.sendCameraFileToGateway.test.js │ │ ├── rtspCamera.streaming.test.js │ │ └── rtspCamera.test.js │ ├── sonos │ │ ├── api │ │ │ └── sonos.controller.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ ├── sonos.init.test.js │ │ │ ├── sonos.onAvTransportEvent.test.js │ │ │ ├── sonos.onVolumeEvent.test.js │ │ │ └── sonos.setValue.test.js │ ├── tasmota │ │ ├── api │ │ │ └── tasmota.controller.test.js │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── device-creation │ │ │ │ ├── AM2301.json │ │ │ │ ├── BL09XX.json │ │ │ │ ├── DHT11.json │ │ │ │ ├── DS18B20.json │ │ │ │ ├── ESP32.json │ │ │ │ ├── SR04.json │ │ │ │ ├── SonoffDualR3.json │ │ │ │ ├── THR320.json │ │ │ │ ├── VL53L0X.json │ │ │ │ ├── colorChannel-1.json │ │ │ │ ├── colorChannel-2.json │ │ │ │ ├── colorChannel-3.json │ │ │ │ ├── colorChannel-4.json │ │ │ │ ├── colorChannel-5.json │ │ │ │ ├── colorScheme.json │ │ │ │ ├── colorSpeed.json │ │ │ │ ├── colorTemperature.json │ │ │ │ ├── counter.json │ │ │ │ ├── dimmer_light.json │ │ │ │ ├── dimmer_switch.json │ │ │ │ ├── energy.json │ │ │ │ ├── power_light_off.json │ │ │ │ ├── power_light_on.json │ │ │ │ ├── power_switch_off.json │ │ │ │ └── power_switch_on.json │ │ │ ├── device-setValue │ │ │ │ ├── mqtt │ │ │ │ │ ├── tasmota.mqtt.setValue-colorChannel.test.js │ │ │ │ │ ├── tasmota.mqtt.setValue-colorScheme.test.js │ │ │ │ │ ├── tasmota.mqtt.setValue-colorSpeed.test.js │ │ │ │ │ ├── tasmota.mqtt.setValue-colorTemperature.test.js │ │ │ │ │ ├── tasmota.mqtt.setValue-dimmer.test.js │ │ │ │ │ ├── tasmota.mqtt.setValue-energy.test.js │ │ │ │ │ ├── tasmota.mqtt.setValue-power-x.test.js │ │ │ │ │ └── tasmota.mqtt.setValue-power.test.js │ │ │ │ └── tasmota.setValue.test.js │ │ │ ├── http │ │ │ │ ├── tasmota.http.getValue.test.js │ │ │ │ ├── tasmota.http.request.test.js │ │ │ │ ├── tasmota.http.scan.test.js │ │ │ │ ├── tasmota.http.setValue.test.js │ │ │ │ ├── tasmota.http.status.test.js │ │ │ │ └── tasmota.http.subStatus.test.js │ │ │ ├── mock │ │ │ │ └── TasmotaProtocolHandlerMock.test.js │ │ │ ├── mqtt │ │ │ │ ├── handle-message │ │ │ │ │ ├── device-creation │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-SonoffDualR3.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorChannel-1.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorChannel-2.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorChannel-3.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorChannel-4.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorChannel-5.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorScheme.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorSpeed.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-colorTemperature.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-counter-feature.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-device_temperature_DS18B20.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-device_temperature_ESP32.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-dimmer_light.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-dimmer_switch.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-distance_SR04.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-distance_VL53L0X.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-energy-feature.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-power_light_off.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-power_light_on.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-power_switch_off.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-power_switch_on.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-temperature_BL09XX.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-temperature_humidity_AM2301.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.deviceCreation-temperature_humidity_DHT11.test.js │ │ │ │ │ │ └── tasmota.mqtt.handleMessage.deviceCreation-temperature_humidity_THR320.test.js │ │ │ │ │ └── new-state │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-arrayValue.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-colorChannel.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-colorScheme.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-colorSpeed.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-colorTemperature.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-dimmer.test.js │ │ │ │ │ │ ├── tasmota.mqtt.handleMessage.newState-energy.test.js │ │ │ │ │ │ └── tasmota.mqtt.handleMessage.newState-power.test.js │ │ │ │ ├── tasmota.mqtt.connect.test.js │ │ │ │ ├── tasmota.mqtt.disconnect.test.js │ │ │ │ └── tasmota.mqtt.handleMessage.test.js │ │ │ ├── tasmota.connect.test.js │ │ │ ├── tasmota.disconnect.test.js │ │ │ ├── tasmota.getDiscoveredDevices.test.js │ │ │ ├── tasmota.getHandler.test.js │ │ │ ├── tasmota.getProtocolFromDevice.test.js │ │ │ ├── tasmota.poll.test.js │ │ │ └── tasmota.scan.test.js │ │ └── tasmota.mock.test.js │ ├── telegram │ │ ├── TelegramApiMock.test.js │ │ ├── index.test.js │ │ └── lib │ │ │ └── messageHandler.test.js │ ├── tp-link │ │ ├── controllers │ │ │ └── getDevices.controller.test.js │ │ ├── devices.json │ │ ├── index.test.js │ │ ├── mocks.test.js │ │ ├── smart-device │ │ │ ├── smart-device.getDevices.test.js │ │ │ ├── smart-device.poll.test.js │ │ │ └── smart-device.setValue.test.js │ │ └── utils │ │ │ └── parseExternalId.test.js │ ├── tuya │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── controllers │ │ │ │ └── tuya.controller.test.js │ │ │ ├── device │ │ │ │ └── feature │ │ │ │ │ ├── tuya.convertFeature.test.js │ │ │ │ │ ├── tuya.convertUnit.test.js │ │ │ │ │ └── tuya.deviceMapping.test.js │ │ │ ├── tuya.connect.test.js │ │ │ ├── tuya.disconnect.test.js │ │ │ ├── tuya.discoverDevices.test.js │ │ │ ├── tuya.getAccessToken.test.js │ │ │ ├── tuya.getConfiguration.test.js │ │ │ ├── tuya.getRefreshToken.test.js │ │ │ ├── tuya.init.test.js │ │ │ ├── tuya.loadDeviceDetails.test.js │ │ │ ├── tuya.loadDevices.test.js │ │ │ ├── tuya.poll.test.js │ │ │ ├── tuya.saveConfiguration.test.js │ │ │ ├── tuya.setTokens.test.js │ │ │ └── tuya.setValue.test.js │ │ └── tuya.mock.test.js │ ├── usb │ │ ├── SerialPortMock.test.js │ │ ├── index.test.js │ │ └── usb.controller.test.js │ ├── xiaomi │ │ ├── DgramMock.test.js │ │ ├── index.test.js │ │ ├── messagesToTest.test.js │ │ ├── xiaomi.controller.test.js │ │ └── xiaomi.test.js │ ├── zigbee2mqtt │ │ ├── api │ │ │ └── zigbee2mqtt.controller.test.js │ │ ├── exposes │ │ │ ├── actionEnumType.test.js │ │ │ ├── binaryType.test.js │ │ │ ├── compositeType.test.js │ │ │ ├── coverEnumType.test.js │ │ │ ├── melodyEnumType.test.js │ │ │ ├── numericType.test.js │ │ │ └── volumeEnumType.test.js │ │ ├── index.test.js │ │ ├── lib │ │ │ ├── backup.test.js │ │ │ ├── backup │ │ │ │ └── base64.backup │ │ │ ├── checkForContainerUpdates.test.js │ │ │ ├── config │ │ │ │ ├── z2m_adapter-deconz_config.yaml │ │ │ │ ├── z2m_adapter-ezsp_config.yaml │ │ │ │ ├── z2m_default_config.yaml │ │ │ │ ├── z2m_mqtt-other_config.yaml │ │ │ │ ├── z2m_mqtt_config.yaml │ │ │ │ └── z2m_port_config.yaml │ │ │ ├── configureContainer.test.js │ │ │ ├── connect.test.js │ │ │ ├── disconnect.test.js │ │ │ ├── findMatchingExpose.test.js │ │ │ ├── getConfiguration.test.js │ │ │ ├── getDiscoveredDevices.test.js │ │ │ ├── getManagedAdapters.test.js │ │ │ ├── getPermitJoin.test.js │ │ │ ├── getSetup.test.js │ │ │ ├── handleMqttMessage.test.js │ │ │ ├── init.test.js │ │ │ ├── installMqttContainer.test.js │ │ │ ├── installZ2mContainer.test.js │ │ │ ├── isEnabled.test.js │ │ │ ├── payloads │ │ │ │ ├── aqara_with_duplicate_feature_mqtt.json │ │ │ │ ├── discovered_devices.json │ │ │ │ ├── event_device_result.json │ │ │ │ ├── mqtt_devices_get.json │ │ │ │ └── single_mqtt_device.json │ │ │ ├── publish.test.js │ │ │ ├── readValue.test.js │ │ │ ├── restoreZ2mBackup.test.js │ │ │ ├── saveConfiguration.test.js │ │ │ ├── saveZ2mBackup.test.js │ │ │ ├── setPermitJoin.test.js │ │ │ ├── setValue.test.js │ │ │ ├── setup.test.js │ │ │ ├── status.test.js │ │ │ └── subscribe.test.js │ │ └── utils │ │ │ ├── convertDevice.test.js │ │ │ ├── convertFeature.test.js │ │ │ ├── feature │ │ │ ├── buildFeatures.test.js │ │ │ ├── completeFeature.test.js │ │ │ └── mapUnit.test.js │ │ │ ├── payloads │ │ │ ├── CCT5015.json │ │ │ ├── ZSS-ZK-THL.json │ │ │ └── index.js │ │ │ └── realZigbee2mqttDevices.test.js │ └── zwavejs-ui │ │ ├── api │ │ └── zwaveJSUI.controller.test.js │ │ ├── index.test.js │ │ ├── lib │ │ ├── exampleData.json │ │ ├── zwaveJSUI.connect.test.js │ │ ├── zwaveJSUI.disconnect.test.js │ │ ├── zwaveJSUI.getConfiguration.test.js │ │ ├── zwaveJSUI.handleNewMessage.test.js │ │ ├── zwaveJSUI.init.test.js │ │ ├── zwaveJSUI.onNewDeviceDiscover.test.js │ │ ├── zwaveJSUI.onNodeValueUpdated.test.js │ │ ├── zwaveJSUI.publish.test.js │ │ ├── zwaveJSUI.saveConfiguration.test.js │ │ └── zwaveJSUI.setValue.test.js │ │ └── utils │ │ └── refineCategory.test.js ├── utils │ ├── buildExpandObject.test.js │ ├── chunks.test.js │ ├── colors.test.js │ ├── compare.test.js │ ├── coordinates.test.js │ ├── device.test.js │ ├── json.test.js │ ├── jwtSecret.test.js │ ├── objects.test.js │ ├── password.test.js │ ├── slugify.test.js │ ├── units.test.js │ └── websocketUtils.test.js └── websockets │ └── index.test.js └── utils ├── accents.js ├── accessToken.js ├── addSelector.js ├── backupKey.js ├── buildExpandObject.js ├── cache.js ├── childProcess.js ├── chunks.js ├── colors.js ├── compare.js ├── constants.js ├── coordinates.js ├── coreErrors.js ├── date.js ├── device.js ├── functionsWrapper.js ├── getConfig.js ├── httpErrors.js ├── json.js ├── jwtSecret.js ├── logger.js ├── objects.js ├── password.js ├── readChunk.js ├── refreshToken.js ├── setDeviceFeature.js ├── slugify.js ├── titleize.js ├── units.js └── websocketUtils.js /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | server/test 3 | dump.rdb 4 | server/*.db 5 | .env 6 | **/.nyc_output 7 | lib-cov 8 | *.seed 9 | *.log 10 | *.out 11 | *.pid 12 | npm-debug.log 13 | *~ 14 | *# 15 | .DS_STORE 16 | .netbeans 17 | .idea 18 | .node_history 19 | node.d.ts 20 | .git 21 | .npmrc 22 | docker 23 | front/build 24 | .github -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | quote_type= single 15 | indent_brace_style= K&R 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Force Unix line endings for most file formats (except binary files) 2 | *.js text eol=lf 3 | *.jsm text eol=lf 4 | *.css text eol=lf 5 | *.html text eol=lf 6 | *.md text eol=lf 7 | *.properties text eol=lf 8 | *.yml text eol=lf 9 | *.json text eol=lf 10 | *.config text eol=lf 11 | *.inc text eol=lf 12 | *.manifest text eol=lf 13 | *.rdf text eol=lf 14 | *.jade text eol=lf 15 | *.coffee text eol=lf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # We provide a way to help Gladys Assistant financially: Gladys Plus. 2 | # It's a package which gives the user additional features, at a 9.99€/month price. 3 | # 4 | # It's thanks to these contributions that this project can be sustainable, so thank you if you helped :) 5 | 6 | custom: ['https://gladysassistant.com/plus', 'https://www.buymeacoffee.com/gladysassistant'] 7 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # 💬 Getting Support from the Gladys Community 2 | 3 | First of all — thank you for using Gladys Assistant! 4 | 5 | If you need help, the best place to ask questions is on our community forums: 6 | 7 | - 🌍 [English Forum](https://en-community.gladysassistant.com/) 8 | - 🇫🇷 [French Forum](https://community.gladysassistant.com/) 9 | 10 | Please **do not open a GitHub issue for support questions** — GitHub is reserved for bug reports. 11 | 12 | For anything else, we’ll be happy to help you on the forum! 13 | 14 | See you there 🙏 15 | -------------------------------------------------------------------------------- /.github/workflows/publish-gladys-plus-production.yml: -------------------------------------------------------------------------------- 1 | name: Publish Gladys Plus front to production 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Run Cloudflare Pages deploy hook 10 | run: curl -X POST "https://api.cloudflare.com/client/v4/pages/webhooks/deploy_hooks/${{ secrets.CLOUDFLARE_PAGES_GLADYS_PLUS_DEPLOY_HOOK }}" 11 | -------------------------------------------------------------------------------- /.github/workflows/relative-ci.yaml: -------------------------------------------------------------------------------- 1 | name: RelativeCI 2 | 3 | on: 4 | workflow_run: 5 | workflows: ['Pull request tests'] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Send webpack stats to RelativeCI 14 | uses: relative-ci/agent-action@v2 15 | with: 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | key: ${{ secrets.RELATIVE_CI_KEY }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.db 3 | *.duckdb 4 | *.duckdb.wal 5 | *.db-shm 6 | *.db-wal 7 | *.dbfile-shm 8 | *.dbfile-wal 9 | apidoc 10 | jsdoc 11 | *.lcov 12 | .nyc_output 13 | .env 14 | build 15 | static 16 | gladys-backups 17 | qemu-* 18 | coverage 19 | persist 20 | stats.json 21 | size-plugin.json 22 | # VSCode 23 | .vscode 24 | # Webstorm 25 | .idea 26 | .tmp 27 | .DS_Store 28 | matter-controller-data 29 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report a vulnerability, you can contact us on Gladys Assistant [french forum](https://community.gladysassistant.com/) or [english forum](https://en-community.gladysassistant.com/) in private, or on the contact form [on our website](https://gladysassistant.com/contact/). 6 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | server: 5 | target: 90% 6 | flags: 7 | - server 8 | 9 | flags: 10 | server: 11 | paths: 12 | - server 13 | -------------------------------------------------------------------------------- /front/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": [ 5 | ["preact-cli/babel", { "modules": "commonjs" }] 6 | ] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /front/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /*.log 4 | *.lock 5 | coverage -------------------------------------------------------------------------------- /front/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | node_modules 4 | -------------------------------------------------------------------------------- /front/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "tabWidth": 2, 6 | "useTabs": false 7 | } 8 | -------------------------------------------------------------------------------- /front/cypress/fixtures/integration/routes/integration/bluetooth/status_not_ready.json: -------------------------------------------------------------------------------- 1 | { 2 | "ready": false, 3 | "scanning": false 4 | } 5 | -------------------------------------------------------------------------------- /front/cypress/fixtures/integration/routes/integration/bluetooth/status_ready.json: -------------------------------------------------------------------------------- 1 | { 2 | "ready": true, 3 | "scanning": false 4 | } 5 | -------------------------------------------------------------------------------- /front/cypress/fixtures/integration/routes/integration/bluetooth/status_scanning.json: -------------------------------------------------------------------------------- 1 | { 2 | "ready": true, 3 | "scanning": true 4 | } 5 | -------------------------------------------------------------------------------- /front/cypress/fixtures/integration/routes/integration/usb/get_available_usb_ports.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "comPath": "/dev/ttyUSB0", 4 | "comVID": "0658", 5 | "comName": "0200" 6 | }, 7 | { 8 | "comPath": "/dev/ttyUSB1", 9 | "comVID": "0478", 10 | "comName": "0910" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /front/cypress/fixtures/integration/routes/integration/zigbee2mqtt/status_not_ready_to_setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "usbConfigured": false, 3 | "mqttExist": false, 4 | "mqttRunning": false, 5 | "zigbee2mqttExist": false, 6 | "zigbee2mqttRunning": false, 7 | "gladysConnected": false, 8 | "zigbee2mqttConnected": false, 9 | "z2mEnabled": false, 10 | "dockerBased": false, 11 | "networkModeValid": false 12 | } 13 | -------------------------------------------------------------------------------- /front/cypress/fixtures/integration/routes/integration/zigbee2mqtt/status_ready_to_setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "usbConfigured": false, 3 | "mqttExist": false, 4 | "mqttRunning": false, 5 | "zigbee2mqttExist": false, 6 | "zigbee2mqttRunning": false, 7 | "gladysConnected": false, 8 | "zigbee2mqttConnected": false, 9 | "z2mEnabled": false, 10 | "dockerBased": true, 11 | "networkModeValid": true 12 | } 13 | -------------------------------------------------------------------------------- /front/netlify.toml: -------------------------------------------------------------------------------- 1 | [[headers]] 2 | for = "/sw.js" 3 | [headers.values] 4 | cache-control = ''' 5 | max-age=0, 6 | no-cache, 7 | no-store, 8 | must-revalidate''' 9 | 10 | [[headers]] 11 | for = "/sw-esm.js" 12 | [headers.values] 13 | cache-control = ''' 14 | max-age=0, 15 | no-cache, 16 | no-store, 17 | must-revalidate''' 18 | 19 | [[redirects]] 20 | from = "/*" 21 | to = "/index.html" 22 | status = 200 23 | -------------------------------------------------------------------------------- /front/old-sw.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('install', function(e) { 2 | self.skipWaiting(); 3 | }); 4 | 5 | self.addEventListener('activate', function(e) { 6 | self.registration 7 | .unregister() 8 | .then(function() { 9 | return self.clients.matchAll(); 10 | }) 11 | .then(function(clients) { 12 | clients.forEach(client => client.navigate(client.url)); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /front/relativeci.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Allow the agent to pick up the current commit message 3 | includeCommitMessage: true, 4 | webpack: { 5 | // Path to Webpack stats JSON file 6 | stats: './stats.json' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /front/src/actions/dashboard/index.js: -------------------------------------------------------------------------------- 1 | function createActions(store) { 2 | const actions = { 3 | setFullScreen(state, fullScreen) { 4 | store.setState({ 5 | fullScreen 6 | }); 7 | } 8 | }; 9 | return actions; 10 | } 11 | 12 | export default createActions; 13 | -------------------------------------------------------------------------------- /front/src/actions/edit-device.js: -------------------------------------------------------------------------------- 1 | import createActionsHouse from './house'; 2 | import createActionsIntegration from './integration'; 3 | 4 | function createActions(store) { 5 | const houseActions = createActionsHouse(store); 6 | const integrationActions = createActionsIntegration(store); 7 | const actions = {}; 8 | return Object.assign({}, houseActions, integrationActions, actions); 9 | } 10 | 11 | export default createActions; 12 | -------------------------------------------------------------------------------- /front/src/actions/integration.js: -------------------------------------------------------------------------------- 1 | const actions = store => ({ 2 | async getIntegrationByName(state, name, podId = null) { 3 | try { 4 | const query = { 5 | pod_id: podId 6 | }; 7 | const currentIntegration = await state.httpClient.get(`/api/v1/service/${name}`, query); 8 | store.setState({ 9 | currentIntegration 10 | }); 11 | } catch (e) { 12 | console.error(e); 13 | } 14 | } 15 | }); 16 | 17 | export default actions; 18 | -------------------------------------------------------------------------------- /front/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/favicon.ico -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-192x192-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-192x192-round.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-512x512-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-512x512-round.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-512x512.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /front/src/assets/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /front/src/assets/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/apple-icon.png -------------------------------------------------------------------------------- /front/src/assets/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/favicon-16x16.png -------------------------------------------------------------------------------- /front/src/assets/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/favicon-32x32.png -------------------------------------------------------------------------------- /front/src/assets/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/icons/favicon-96x96.png -------------------------------------------------------------------------------- /front/src/assets/images/home-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/images/home-icon.png -------------------------------------------------------------------------------- /front/src/assets/images/welcome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/images/welcome.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/airplay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/airplay.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/alexa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/alexa.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/bluetooth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/bluetooth.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/broadlink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/broadlink.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/caldav.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/caldav.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/callmebot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/callmebot.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/darksky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/darksky.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/enedis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/enedis.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/ewelink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/ewelink.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/free-mobile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/free-mobile.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/google-cast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/google-cast.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/google-home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/google-home.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/homekit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/homekit.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/lan-manager.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/lan-manager.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/matter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/matter.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/melcloud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/melcloud.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/mqtt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/mqtt.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/netatmo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/netatmo.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/nextcloud-talk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/nextcloud-talk.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/node-red.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/node-red.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/openai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/openai.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/openweather.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/openweather.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/owntracks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/owntracks.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/philips-hue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/philips-hue.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/rtsp-camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/rtsp-camera.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/sonos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/sonos.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/tasmota.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/tasmota.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/telegram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/telegram.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/tp-link.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/tp-link.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/tuya.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/tuya.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/wemo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/wemo.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/xiaomi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/xiaomi.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/zigbee2mqtt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/zigbee2mqtt.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/cover/zwave-js-ui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/cover/zwave-js-ui.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NAMain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NAMain.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NAModule1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NAModule1.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NAModule2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NAModule2.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NAModule3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NAModule3.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NAModule4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NAModule4.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NAPlug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NAPlug.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NATherm1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NATherm1.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/devices/netatmo/netatmo-NRV.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/devices/netatmo/netatmo-NRV.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/logos/logo_mqtt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/logos/logo_mqtt.png -------------------------------------------------------------------------------- /front/src/assets/integrations/logos/logo_node-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/logos/logo_node-red.png -------------------------------------------------------------------------------- /front/src/assets/integrations/logos/logo_zigbee2mqtt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/logos/logo_zigbee2mqtt.png -------------------------------------------------------------------------------- /front/src/assets/integrations/zwavejs-ui/zwavejs-ui-gateway-configuration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/zwavejs-ui/zwavejs-ui-gateway-configuration.jpg -------------------------------------------------------------------------------- /front/src/assets/integrations/zwavejs-ui/zwavejs-ui-mqtt-configuration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/integrations/zwavejs-ui/zwavejs-ui-mqtt-configuration.jpg -------------------------------------------------------------------------------- /front/src/assets/leaflet/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/leaflet/layers-2x.png -------------------------------------------------------------------------------- /front/src/assets/leaflet/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/leaflet/layers.png -------------------------------------------------------------------------------- /front/src/assets/leaflet/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/leaflet/marker-icon-2x.png -------------------------------------------------------------------------------- /front/src/assets/leaflet/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/leaflet/marker-icon.png -------------------------------------------------------------------------------- /front/src/assets/leaflet/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/leaflet/marker-shadow.png -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1125-2436.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1125-2436.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1136-640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1136-640.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1170-2532.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1170-2532.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1242-2208.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1242-2208.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1242-2688.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1242-2688.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1284-2778.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1284-2778.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1334-750.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1334-750.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1536-2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1536-2048.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1620-2160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1620-2160.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1668-2224.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1668-2224.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1668-2388.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1668-2388.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-1792-828.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-1792-828.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2048-1536.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2048-1536.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2048-2732.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2048-2732.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2160-1620.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2160-1620.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2208-1242.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2208-1242.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2224-1668.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2224-1668.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2388-1668.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2388-1668.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2436-1125.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2436-1125.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2532-1170.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2532-1170.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2688-1242.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2688-1242.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2732-2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2732-2048.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-2778-1284.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-2778-1284.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-640-1136.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-640-1136.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-750-1334.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-750-1334.jpg -------------------------------------------------------------------------------- /front/src/assets/splash/apple-splash-828-1792.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/assets/splash/apple-splash-828-1792.jpg -------------------------------------------------------------------------------- /front/src/components/boxs/alarm/countdown.css: -------------------------------------------------------------------------------- 1 | .countdown { 2 | font-size: 5em; 3 | color: #333; 4 | font-weight: bold; 5 | text-align: center; 6 | } 7 | 8 | .countdownTimer { 9 | display: inline-block; 10 | padding: 10px; 11 | transition: transform 0.5s, opacity 0.5s; 12 | } 13 | 14 | .countdownTimer.updated { 15 | transform: scale(0.9); 16 | opacity: 0.7; 17 | } 18 | -------------------------------------------------------------------------------- /front/src/components/boxs/alarm/style.css: -------------------------------------------------------------------------------- 1 | .alarmActionButton { 2 | height: 6rem; 3 | line-height: 16px; 4 | } 5 | 6 | .alarmActionIcon { 7 | font-size: 30px !important; 8 | } 9 | -------------------------------------------------------------------------------- /front/src/components/boxs/camera/style.css: -------------------------------------------------------------------------------- 1 | .noImageToShowError { 2 | background-color: #34495e; 3 | color: white; 4 | height: 244px; 5 | padding: 12px; 6 | padding-bottom: 0px; 7 | margin-bottom: 0px; 8 | display: table-cell; 9 | vertical-align: middle; 10 | border-top-left-radius: 3px; 11 | border-top-right-radius: 3px; 12 | } 13 | -------------------------------------------------------------------------------- /front/src/components/boxs/clock/ClockTypes.js: -------------------------------------------------------------------------------- 1 | export const CLOCK_TYPES = { 2 | ANALOG: 'analog', 3 | DIGITAL: 'digital' 4 | }; 5 | 6 | export const CLOCK_TYPES_LIST = [CLOCK_TYPES.ANALOG, CLOCK_TYPES.DIGITAL]; 7 | -------------------------------------------------------------------------------- /front/src/components/boxs/device-in-room/device-features/sensor-value/NoRecentValueBadge.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | 3 | const NoRecentValueBadge = () => ( 4 | 5 | 6 | 7 | ); 8 | 9 | export default NoRecentValueBadge; 10 | -------------------------------------------------------------------------------- /front/src/components/boxs/device-in-room/device-features/sensor-value/RawDeviceValue.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | 3 | const RawDeviceValue = ({ deviceFeature }) => ( 4 |
5 | {deviceFeature.last_value === null && } 6 | {deviceFeature.last_value !== null && ( 7 | 8 | {`${deviceFeature.last_value} `} 9 | 10 | 11 | )} 12 |
13 | ); 14 | 15 | export default RawDeviceValue; 16 | -------------------------------------------------------------------------------- /front/src/components/boxs/device-in-room/device-features/sensor-value/TextDeviceValue.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | 3 | const RawDeviceValue = ({ deviceFeature }) => ( 4 |
5 | {deviceFeature.last_value_string === null && } 6 | {deviceFeature.last_value_string !== null && {deviceFeature.last_value_string}} 7 |
8 | ); 9 | 10 | export default RawDeviceValue; 11 | -------------------------------------------------------------------------------- /front/src/components/boxs/device-in-room/style.css: -------------------------------------------------------------------------------- 1 | .loadingSkeleton { 2 | height: 1.2em; 3 | background: #eee; 4 | background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%); 5 | border-radius: 5px; 6 | background-size: 200% 100%; 7 | animation: 1.5s shine linear infinite; 8 | } 9 | 10 | @keyframes shine { 11 | to { 12 | background-position-x: -200%; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /front/src/components/boxs/edf-tempo/style.css: -------------------------------------------------------------------------------- 1 | .h4Title { 2 | font-size: 16px; 3 | } 4 | 5 | .hourDisplay { 6 | width: 100%; 7 | padding: 0.5rem 1rem; 8 | font-size: 1.25rem; 9 | line-height: 1.5; 10 | border-radius: 0.3rem; 11 | text-align: 'center'; 12 | } 13 | 14 | .peakHour { 15 | color: #f1c40f; 16 | background-color: transparent; 17 | background-image: none; 18 | border-color: #f1c40f; 19 | border: 1px solid; 20 | } 21 | 22 | .offPeakHour { 23 | border: 1px solid; 24 | } 25 | -------------------------------------------------------------------------------- /front/src/components/boxs/scene/style.css: -------------------------------------------------------------------------------- 1 | .btnLoading { 2 | &::after { 3 | border: 2px solid #5eba00; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /front/src/components/device/view/DeviceFeature.jsx: -------------------------------------------------------------------------------- 1 | import get from 'get-value'; 2 | import { Text } from 'preact-i18n'; 3 | 4 | import { DeviceFeatureCategoriesIcon } from '../../../utils/consts'; 5 | 6 | const DeviceFeature = ({ feature }) => ( 7 | 8 | 9 |
10 | 11 |
12 |
13 | ); 14 | 15 | export default DeviceFeature; 16 | -------------------------------------------------------------------------------- /front/src/components/gateway/GatewayAccountExpired.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import style from './style.css'; 3 | 4 | const GatewayAccountExpired = ({}) => ( 5 |
6 | 7 |

8 | 9 |

10 |
11 | ); 12 | 13 | export default GatewayAccountExpired; 14 | -------------------------------------------------------------------------------- /front/src/components/gateway/style.css: -------------------------------------------------------------------------------- 1 | .gatewayExpiredDivBox { 2 | width: 60%; 3 | max-width: 400px; 4 | margin-left: auto; 5 | margin-right: auto; 6 | margin-top: 120px; 7 | text-align: center; 8 | } 9 | 10 | .gatewayExpiredText { 11 | margin-top: 20px; 12 | } 13 | 14 | .gatewayExpiredImage { 15 | margin-left: auto; 16 | margin-right: auto; 17 | display: block; 18 | width: 100%; 19 | max-width: 300px; 20 | } 21 | -------------------------------------------------------------------------------- /front/src/components/icons/SvgIcon.jsx: -------------------------------------------------------------------------------- 1 | import IconSprite from '/assets/icons/icon-sprite.svg'; 2 | 3 | const TablerIcon = ({ icon }) => ( 4 | 13 | 14 | 15 | ); 16 | 17 | export default TablerIcon; 18 | -------------------------------------------------------------------------------- /front/src/components/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | 3 | const NOT_MAIN_PAGES = ['/login']; 4 | 5 | const notMainPages = currentUrl => { 6 | const found = NOT_MAIN_PAGES.find(page => { 7 | return currentUrl.startsWith(page); 8 | }); 9 | if (found) { 10 | return true; 11 | } 12 | return false; 13 | }; 14 | 15 | const Layout = ({ children, ...props }) => ( 16 |
17 |
{children}
18 |
19 | ); 20 | 21 | export default Layout; 22 | -------------------------------------------------------------------------------- /front/src/components/router/Redirect.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { route } from 'preact-router'; 3 | 4 | export default class Redirect extends Component { 5 | componentWillMount() { 6 | route(this.props.to, true); 7 | } 8 | 9 | render() { 10 | return null; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /front/src/config/i18n/index.js: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import fr from './fr.json'; 3 | import de from './de.json'; 4 | import { AVAILABLE_LANGUAGES } from '../../../../server/utils/constants'; 5 | 6 | export default { [AVAILABLE_LANGUAGES.FR]: fr, [AVAILABLE_LANGUAGES.EN]: en, [AVAILABLE_LANGUAGES.DE]: de }; 7 | -------------------------------------------------------------------------------- /front/src/config/integrations/calendars.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "caldav", 4 | "img": "/assets/integrations/cover/caldav.jpg" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /front/src/config/integrations/weathers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "openWeather", 4 | "img": "/assets/integrations/cover/openweather.jpg" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /front/src/index.js: -------------------------------------------------------------------------------- 1 | import '@gladysassistant/theme-optimized/dashboard.css'; 2 | import 'dayjs/locale/en'; 3 | import 'dayjs/locale/fr'; 4 | import 'dayjs/locale/de'; 5 | import './style'; 6 | import App from './components/app'; 7 | 8 | export default App; 9 | -------------------------------------------------------------------------------- /front/src/routes/dashboard/new-dashboard/style.css: -------------------------------------------------------------------------------- 1 | .containerWithMargin { 2 | margin-top: 3rem; 3 | } 4 | 5 | @media (max-width: 768px) { 6 | .backButtonDiv { 7 | margin-bottom: 1rem; 8 | max-width: 24rem; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/airplay/device-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DeviceTab from './DeviceTab'; 3 | import AirplayPage from '../AirplayPage'; 4 | 5 | const DevicePage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(DevicePage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/airplay/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .tuyaListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/airplay/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/airplay/discover-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DiscoverTab from './DiscoverTab'; 3 | import AirplayPage from '../AirplayPage'; 4 | 5 | const AirplayDiscoverPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(AirplayDiscoverPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/airplay/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .airplayListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/alexa-gateway/Layout.jsx: -------------------------------------------------------------------------------- 1 | const Layout = ({ children }) => ( 2 |
3 |
4 |
5 |
6 |
7 |
{children}
8 |
9 |
10 |
11 |
12 |
13 | ); 14 | 15 | export default Layout; 16 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/alexa-gateway/style.css: -------------------------------------------------------------------------------- 1 | .colWidth { 2 | max-width: 35rem; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/bluetooth/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | 4 | import style from './style.css'; 5 | 6 | const EmptyState = ({ id }) => ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | 14 | export default EmptyState; 15 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/bluetooth/settings-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | 3 | import BluetoothPage from '../BluetoothPage'; 4 | import BluetoothSettingsTab from './BluetoothSettingsTab'; 5 | import actions from '../commons/actions'; 6 | 7 | const BluetoothSettingsPage = props => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default connect('user,httpClient,bluetoothStatus', actions)(BluetoothSettingsPage); 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/bluetooth/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | margin-bottom: 35px; 4 | } 5 | 6 | .bluetoothListBody { 7 | min-height: 200px; 8 | } 9 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/broadlink/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | 4 | import style from './style.css'; 5 | 6 | const EmptyState = ({ id }) => ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | 14 | export default EmptyState; 15 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/broadlink/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | margin-bottom: 35px; 4 | } 5 | 6 | .emptyDiv { 7 | min-height: 200px; 8 | } 9 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/caldav/account-page/AccountTab.css: -------------------------------------------------------------------------------- 1 | .errorMessage { 2 | margin-bottom: 0; 3 | font-style: 'italic'; 4 | } 5 | 6 | .successMessage { 7 | margin-bottom: 10px; 8 | } 9 | 10 | .button { 11 | margin-right: 10px; 12 | } 13 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/caldav/share-page/ShareTab.css: -------------------------------------------------------------------------------- 1 | .switchGroup { 2 | margin-bottom: 10px; 3 | } 4 | 5 | .switchLabel { 6 | display: block; 7 | margin-bottom: 5px; 8 | } 9 | 10 | .switchIndicator { 11 | margin-right: 5px; 12 | } 13 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/caldav/sync-page/SyncTab.css: -------------------------------------------------------------------------------- 1 | .switchGroup { 2 | margin-bottom: 10px; 3 | } 4 | 5 | .switchLabel { 6 | display: block; 7 | margin-bottom: 5px; 8 | } 9 | 10 | .switchIndicator { 11 | margin-right: 5px; 12 | } 13 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/callmebot/setup-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import CallMeBotPage from '../CallMeBot'; 3 | import SetupTab from './SetupTab'; 4 | 5 | const CallMeBotSetupPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user,httpClient')(CallMeBotSetupPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/enedis-gateway/consts.js: -------------------------------------------------------------------------------- 1 | const DEVICE_PARAMS = { 2 | CONTRACT_TYPE: 'CONTRACT_TYPE', 3 | PRICE_PER_KWH: 'PRICE_PER_KWH', 4 | PRICE_CURRENCY: 'PRICE_CURRENCY', 5 | OFF_PEAK_HOURS: 'OFF_PEAK_HOURS', 6 | OFF_PEAK_HOURS_PRICE_PER_KWH: 'OFF_PEAK_HOURS_PRICE_PER_KWH', 7 | PEAK_HOURS_PRICE_PER_KWH: 'PEAK_HOURS_PRICE_PER_KWH', 8 | LAST_REFRESH: 'LAST_REFRESH', 9 | NUMBER_OF_STATES: 'NUMBER_OF_STATES' 10 | }; 11 | 12 | export { DEVICE_PARAMS }; 13 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/enedis-gateway/enedis-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/front/src/routes/integration/all/enedis-gateway/enedis-button.png -------------------------------------------------------------------------------- /front/src/routes/integration/all/ewelink/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .eweLinkListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/ewelink/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/ewelink/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .eweLinkListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-cast/device-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DeviceTab from './DeviceTab'; 3 | import GoogleCastPage from '../GoogleCastPage'; 4 | 5 | const DevicePage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(DevicePage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-cast/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .tuyaListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-cast/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-cast/discover-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DiscoverTab from './DiscoverTab'; 3 | import GoogleCastPage from '../GoogleCastPage'; 4 | 5 | const GoogleCastDiscoverPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(GoogleCastDiscoverPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-cast/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .googleCastListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-home-gateway/Layout.jsx: -------------------------------------------------------------------------------- 1 | const Layout = ({ children }) => ( 2 |
3 |
4 |
5 |
6 |
7 |
{children}
8 |
9 |
10 |
11 |
12 |
13 | ); 14 | 15 | export default Layout; 16 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/google-home-gateway/style.css: -------------------------------------------------------------------------------- 1 | .colWidth { 2 | max-width: 35rem; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/homekit/style.css: -------------------------------------------------------------------------------- 1 | .buttonDescription { 2 | margin-top: 1em; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/lan-manager/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | 4 | import style from './style.css'; 5 | 6 | const EmptyState = ({ id }) => ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | 14 | export default EmptyState; 15 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/lan-manager/settings-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | 3 | import LANManagerPage from '../LANManagerPage'; 4 | import LANManagerSettingsTab from './LANManagerSettingsTab'; 5 | 6 | const LANManagerSettingsPage = props => ( 7 | 8 | 9 | 10 | ); 11 | 12 | export default connect('user,httpClient')(LANManagerSettingsPage); 13 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/lan-manager/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | margin-bottom: 35px; 4 | } 5 | 6 | .lanManagerListBody { 7 | min-height: 200px; 8 | } 9 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/melcloud/device-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DeviceTab from './DeviceTab'; 3 | import MELCloudPage from '../MELCloudPage'; 4 | 5 | const DevicePage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(DevicePage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/melcloud/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .melcloudListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/melcloud/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/melcloud/discover-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DiscoverTab from './DiscoverTab'; 3 | import MELCloudPage from '../MELCloudPage'; 4 | 5 | const MELCloudDiscoverPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(MELCloudDiscoverPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/melcloud/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .melcloudListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/melcloud/setup-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import SetupTab from './SetupTab'; 3 | import MELCloudPage from '../MELCloudPage'; 4 | 5 | const MELCloudSetupPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(MELCloudSetupPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/mqtt/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyDiv { 2 | min-height: 200px; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/netatmo/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .netatmoListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/netatmo/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/netatmo/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .netatmoListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/netatmo/style.css: -------------------------------------------------------------------------------- 1 | .device-image-container { 2 | max-width: 75%; 3 | margin: 0 auto; 4 | display: block; 5 | } -------------------------------------------------------------------------------- /front/src/routes/integration/all/node-red/setup-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import NodeRedPage from '../NodeRedPage'; 3 | import SetupTab from './SetupTab'; 4 | 5 | const NodeRedSetupPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user,session,httpClient', {})(NodeRedSetupPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/philips-hue/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyDiv { 2 | min-height: 200px 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/philips-hue/setup-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyDiv { 2 | min-height: 200px 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/rtsp-camera/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/rtsp-camera/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | margin-bottom: 35px; 4 | } 5 | 6 | .rtspCameraListBody { 7 | min-height: 200px; 8 | } 9 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/sonos/device-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DeviceTab from './DeviceTab'; 3 | import SonosPage from '../SonosPage'; 4 | 5 | const DevicePage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(DevicePage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/sonos/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .tuyaListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/sonos/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/sonos/discover-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DiscoverTab from './DiscoverTab'; 3 | import SonosPage from '../SonosPage'; 4 | 5 | const SonosDiscoverPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(SonosDiscoverPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/sonos/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .sonosListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tasmota/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = () => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tasmota/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 40px; 3 | } 4 | 5 | .tasmotaListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tasmota/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 29px; 3 | } 4 | 5 | .tasmotaListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tp-link/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyDiv { 2 | min-height: 200px 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tuya/device-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DeviceTab from './DeviceTab'; 3 | import TuyaPage from '../TuyaPage'; 4 | 5 | const DevicePage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(DevicePage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tuya/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | 5 | .tuyaListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tuya/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tuya/discover-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import DiscoverTab from './DiscoverTab'; 3 | import TuyaPage from '../TuyaPage'; 4 | 5 | const TuyaDiscoverPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(TuyaDiscoverPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tuya/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .tuyaListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/tuya/setup-page/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import SetupTab from './SetupTab'; 3 | import TuyaPage from '../TuyaPage'; 4 | 5 | const TuyaSetupPage = props => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default connect('user', {})(TuyaSetupPage); 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/xiaomi/edit-page/EditPage.jsx: -------------------------------------------------------------------------------- 1 | import UpdateDevice from '../../../../../components/device'; 2 | 3 | const EditPage = ({ children, ...props }) => ; 4 | 5 | export default EditPage; 6 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/xiaomi/style.css: -------------------------------------------------------------------------------- 1 | .emptyDiv { 2 | min-height: 200px; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zigbee2mqtt/device-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zigbee2mqtt/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .zigbee2mqttListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zigbee2mqtt/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = () => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zigbee2mqtt/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .zigbee2mqttListBody { 6 | min-height: 200px 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zigbee2mqtt/setup-page/constants.js: -------------------------------------------------------------------------------- 1 | const MQTT_MODE = { 2 | LOCAL: 'local', 3 | EXTERNAL: 'external' 4 | }; 5 | 6 | const SETUP_MODES = { 7 | LOCAL: 'local', 8 | REMOTE: 'remote' 9 | }; 10 | 11 | export { MQTT_MODE, SETUP_MODES }; 12 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zwavejs-ui/device-page/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { connect } from 'unistore/preact'; 3 | import DeviceTab from './DeviceTab'; 4 | import ZwaveJSUIPage from '../ZwaveJSUIPage'; 5 | 6 | class DevicePage extends Component { 7 | render(props, {}) { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | 16 | export default connect('user', {})(DevicePage); 17 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zwavejs-ui/device-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 35px; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zwavejs-ui/discover-page/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { MarkupText } from 'preact-i18n'; 2 | import cx from 'classnames'; 3 | import style from './style.css'; 4 | 5 | const EmptyState = ({}) => ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zwavejs-ui/discover-page/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { connect } from 'unistore/preact'; 3 | import DiscoverTab from './DiscoverTab'; 4 | import ZwaveJSUIPage from '../ZwaveJSUIPage'; 5 | 6 | class ZwaveJSUIDiscoverPage extends Component { 7 | render(props) { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | 16 | export default connect('user', {})(ZwaveJSUIDiscoverPage); 17 | -------------------------------------------------------------------------------- /front/src/routes/integration/all/zwavejs-ui/discover-page/style.css: -------------------------------------------------------------------------------- 1 | .emptyStateDivBox { 2 | margin-top: 89px; 3 | } 4 | 5 | .zwaveJSUIListBody { 6 | min-height: 200px; 7 | } 8 | -------------------------------------------------------------------------------- /front/src/routes/locked/style.css: -------------------------------------------------------------------------------- 1 | .lockedContainer { 2 | margin-top: 3rem; 3 | } 4 | 5 | .lockedMainCol { 6 | max-width: 30rem; 7 | } 8 | 9 | .lockedContainer .cardTitle { 10 | font-weight: 700; 11 | } 12 | 13 | .lockedButton { 14 | height: 4rem; 15 | } 16 | 17 | .lockedInput { 18 | height: 3rem; 19 | } 20 | 21 | .lockedDeleteButton { 22 | width: 4rem; 23 | } 24 | -------------------------------------------------------------------------------- /front/src/routes/login-gateway/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { connect } from 'unistore/preact'; 3 | import actions from '../../actions/login/loginGateway'; 4 | import LoginGatewayPage from './LoginGatewayPage'; 5 | 6 | class Login extends Component { 7 | componentWillMount() { 8 | this.props.init(this.props.return_url); 9 | } 10 | 11 | render(props, {}) { 12 | return ; 13 | } 14 | } 15 | 16 | export default connect('gatewayLoginStep2,gatewayLoginStatus,gatewayLoginError', actions)(Login); 17 | -------------------------------------------------------------------------------- /front/src/routes/login/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { connect } from 'unistore/preact'; 3 | import actions from '../../actions/login/login'; 4 | import LoginPage from './LoginPage'; 5 | 6 | class Login extends Component { 7 | componentWillMount() { 8 | this.props.checkIfInstanceIsConfigured(); 9 | } 10 | 11 | render({}, {}) { 12 | return ; 13 | } 14 | } 15 | 16 | export default connect('', actions)(Login); 17 | -------------------------------------------------------------------------------- /front/src/routes/map/style.css: -------------------------------------------------------------------------------- 1 | .userIconImage { 2 | border-radius: 50%; 3 | border: solid; 4 | border-color: #5eba00; 5 | } 6 | 7 | .houseIconImage { 8 | } 9 | 10 | .containerWithMargin { 11 | margin-top: 3rem; 12 | } 13 | -------------------------------------------------------------------------------- /front/src/routes/scene/EmptyState.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | import style from './style.css'; 3 | 4 | const EmptyState = ({}) => ( 5 |
6 | 7 |

8 | 9 |

10 |
11 | ); 12 | 13 | export default EmptyState; 14 | -------------------------------------------------------------------------------- /front/src/routes/scene/constant.js: -------------------------------------------------------------------------------- 1 | const MAX_LENGTH_TAG = 30; 2 | 3 | module.exports.MAX_LENGTH_TAG = MAX_LENGTH_TAG; 4 | -------------------------------------------------------------------------------- /front/src/routes/scene/duplicate-scene/style.css: -------------------------------------------------------------------------------- 1 | .containerWithMargin { 2 | margin-top: 3rem; 3 | } 4 | 5 | 6 | .iconContainer { 7 | margin-top: 1rem; 8 | height: 10rem; 9 | overflow: scroll; 10 | } 11 | 12 | .iconDiv { 13 | padding: 5px; 14 | width: 35px; 15 | margin-bottom: 8px; 16 | } 17 | 18 | .iconDivChecked { 19 | background-color: #f5f7fb; 20 | border-radius: 4px; 21 | } 22 | 23 | .iconLabel { 24 | cursor: pointer; 25 | margin-bottom: 0; 26 | } 27 | 28 | .iconInput { 29 | position: absolute; 30 | z-index: -1; 31 | opacity: 0; 32 | } 33 | -------------------------------------------------------------------------------- /front/src/routes/scene/edit-scene/actions/CalendarIsEventRunning.css: -------------------------------------------------------------------------------- 1 | @media (min-width: 576px) { 2 | .calendarEventIsComingMarginInputMargin { 3 | margin-top: 27px; 4 | } 5 | } 6 | 7 | @media (max-width: 576px) { 8 | .calendarEventIsComingGroupMargin { 9 | margin-top: 12px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /front/src/routes/scene/edit-scene/actions/CheckTime.css: -------------------------------------------------------------------------------- 1 | .clearButtonCustom::after { 2 | background-color: #485056; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/scene/edit-scene/actions/DeviceSetValue.css: -------------------------------------------------------------------------------- 1 | .explanationText { 2 | font-size: 12px; 3 | margin-bottom: .375rem; 4 | } 5 | 6 | .valueTypeTab { 7 | display: flex; 8 | margin: 0; 9 | } 10 | 11 | .valueTypeLink { 12 | width: 50%; 13 | } 14 | -------------------------------------------------------------------------------- /front/src/routes/scene/edit-scene/actions/only-continue-if/Condition.css: -------------------------------------------------------------------------------- 1 | .conditionTagify { 2 | min-height: 2.375rem; 3 | display: flex; 4 | align-items: center; 5 | } 6 | 7 | .explanationText { 8 | font-size: 12px; 9 | margin-bottom: .375rem; 10 | } 11 | -------------------------------------------------------------------------------- /front/src/routes/scene/edit-scene/triggers/GladysStartTrigger.jsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'unistore/preact'; 2 | import { Text } from 'preact-i18n'; 3 | 4 | const GladysStartTrigger = () => ( 5 |
6 |
7 |
8 | 9 |
10 |
11 |
12 | ); 13 | 14 | export default connect('', {})(GladysStartTrigger); 15 | -------------------------------------------------------------------------------- /front/src/routes/scene/edit-scene/triggers/style.css: -------------------------------------------------------------------------------- 1 | @media (min-width: 576px) { 2 | .calendarEventIsComingMarginInputMargin { 3 | margin-top: 27px; 4 | } 5 | } 6 | 7 | @media (max-width: 576px) { 8 | .calendarEventIsComingGroupMargin { 9 | margin-top: 12px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /front/src/routes/scene/new-scene/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { connect } from 'unistore/preact'; 3 | import actions from '../../../actions/createScene'; 4 | import NewScenePage from './NewScenePage'; 5 | 6 | class NewScene extends Component { 7 | componentDidMount() { 8 | this.props.initScene(); 9 | } 10 | 11 | render(props, {}) { 12 | return ; 13 | } 14 | } 15 | 16 | export default connect('newScene,newSceneErrors,createSceneStatus', actions)(NewScene); 17 | -------------------------------------------------------------------------------- /front/src/routes/scene/new-scene/style.css: -------------------------------------------------------------------------------- 1 | .containerWithMargin { 2 | margin-top: 3rem; 3 | } 4 | 5 | .iconContainer { 6 | margin-top: 1rem; 7 | height: 10rem; 8 | overflow: scroll; 9 | } 10 | 11 | .iconDiv { 12 | padding: 5px; 13 | width: 35px; 14 | margin-bottom: 8px; 15 | } 16 | 17 | .iconDivChecked { 18 | background-color: #f5f7fb; 19 | border-radius: 4px; 20 | } 21 | 22 | .iconLabel { 23 | cursor: pointer; 24 | margin-bottom: 0; 25 | } 26 | 27 | .iconInput { 28 | position: absolute; 29 | z-index: -1; 30 | opacity: 0; 31 | } 32 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-background-jobs/style.css: -------------------------------------------------------------------------------- 1 | .errorDiv { 2 | padding-top: 1rem; 3 | width: 40rem; 4 | } 5 | 6 | @media (max-width: 1280px) { 7 | .errorDiv { 8 | width: 30rem; 9 | } 10 | } 11 | 12 | @media (max-width: 992px) { 13 | .errorDiv { 14 | width: 30rem; 15 | } 16 | } 17 | 18 | @media (max-width: 768px) { 19 | .errorDiv { 20 | width: 20rem; 21 | } 22 | } 23 | 24 | .errorDirectDiv { 25 | overflow-wrap: break-word; 26 | word-wrap: break-word; 27 | white-space: pre-line; 28 | hyphens: auto; 29 | } 30 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-backup/style.css: -------------------------------------------------------------------------------- 1 | .divLoading { 2 | min-height: 300px; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-gateway/style.css: -------------------------------------------------------------------------------- 1 | .userNameCell { 2 | max-width: 300px; 3 | overflow-x: scroll; 4 | } 5 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-house/EmptySearch.jsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'preact-i18n'; 2 | 3 | const EmptySearch = ({}) => ( 4 |
5 | 6 |
7 | ); 8 | 9 | export default EmptySearch; 10 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-house/House.jsx: -------------------------------------------------------------------------------- 1 | import EditHouse from '../../../components/house/EditHouseComponent'; 2 | 3 | const House = ({ children, ...props }) => ( 4 |
5 |

{props.house.name}

6 |
7 | 8 |
9 |
10 | ); 11 | 12 | export default House; 13 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-session/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import { connect } from 'unistore/preact'; 3 | import SessionPage from './SessionsPage'; 4 | import actions from '../../../actions/session'; 5 | 6 | class SettingsSessions extends Component { 7 | componentWillMount() { 8 | this.props.getSessions(); 9 | } 10 | 11 | render(props, {}) { 12 | return ; 13 | } 14 | } 15 | 16 | export default connect('user,sessions,sessionsGetStatus', actions)(SettingsSessions); 17 | -------------------------------------------------------------------------------- /front/src/routes/settings/settings-system/style.css: -------------------------------------------------------------------------------- 1 | .textDecorationNone { 2 | text-decoration: none !important; 3 | } 4 | -------------------------------------------------------------------------------- /front/src/routes/signup/5-success/index.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | import SignupLayout from '../layout'; 3 | import SuccessTab from './SuccessTab'; 4 | 5 | class Success extends Component { 6 | componentDidMount() {} 7 | 8 | render({}, {}) { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | export default Success; 18 | -------------------------------------------------------------------------------- /front/src/routes/signup/style.css: -------------------------------------------------------------------------------- 1 | .equal { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | display: flex; 6 | flex-wrap: wrap; 7 | } 8 | 9 | .equal > [class*='col-'] { 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | 14 | @media (max-width: 768px) { 15 | .signupTitle { 16 | font-size: 1.5rem; 17 | } 18 | } 19 | 20 | .navLinkWithoutCursor { 21 | cursor: default; 22 | } 23 | 24 | @media (max-width: 768px) { 25 | .navResponsive .nav-item { 26 | display: none; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /front/src/utils/bytesFormat.js: -------------------------------------------------------------------------------- 1 | import get from 'get-value'; 2 | 3 | const SIZES = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte']; 4 | 5 | function bytesFormatter(size, lang, dictionary) { 6 | const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)); 7 | const sizeCode = SIZES[i]; 8 | const sizeName = get(dictionary, `deviceFeatureUnitShort.${sizeCode}`); 9 | return `${(size / Math.pow(1024, i)).toFixed(2) * 1} ${sizeName}`; 10 | } 11 | 12 | export { bytesFormatter }; 13 | -------------------------------------------------------------------------------- /front/src/utils/color.js: -------------------------------------------------------------------------------- 1 | function isBright(color) { 2 | const rgb = parseInt(color.substring(1), 16); // convert rrggbb to decimal 3 | const r = (rgb >> 16) & 0xff; // extract red 4 | const g = (rgb >> 8) & 0xff; // extract green 5 | const b = (rgb >> 0) & 0xff; // extract blue 6 | 7 | return (299 * r + 587 * g + 114 * b) / 1000 > 130; 8 | } 9 | 10 | export { isBright }; 11 | -------------------------------------------------------------------------------- /front/src/utils/keyValueStore.js: -------------------------------------------------------------------------------- 1 | const KEY_VALUE_STORE_PREFIX = 'gladys_assistant_v1_'; 2 | 3 | const keyValStore = { 4 | get(key) { 5 | return localStorage.getItem(`${KEY_VALUE_STORE_PREFIX}${key}`); 6 | }, 7 | set(key, val) { 8 | return localStorage.setItem(`${KEY_VALUE_STORE_PREFIX}${key}`, val); 9 | }, 10 | clear() { 11 | return localStorage.clear(); 12 | } 13 | }; 14 | 15 | export default keyValStore; 16 | -------------------------------------------------------------------------------- /front/src/utils/url.js: -------------------------------------------------------------------------------- 1 | function isUrlInArray(url, array) { 2 | let splittedUrl = url.split('?')[0]; 3 | if (splittedUrl.substring(splittedUrl.length - 1) === '/') { 4 | splittedUrl = splittedUrl.substring(0, splittedUrl.length - 1); 5 | } 6 | if (array.includes(splittedUrl)) { 7 | return true; 8 | } 9 | return false; 10 | } 11 | 12 | export { isUrlInArray }; 13 | -------------------------------------------------------------------------------- /front/src/utils/validateEmail.js: -------------------------------------------------------------------------------- 1 | function validateEmail(email) { 2 | return email.match(/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/); 3 | } 4 | 5 | export default validateEmail; 6 | -------------------------------------------------------------------------------- /front/src/utils/validator.js: -------------------------------------------------------------------------------- 1 | export function validateEmail(email) { 2 | const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; // eslint-disable-line 3 | return re.test(String(email).toLowerCase()); 4 | } 5 | -------------------------------------------------------------------------------- /front/src/utils/withIntlAsProp.js: -------------------------------------------------------------------------------- 1 | import { IntlContext } from 'preact-i18n'; 2 | 3 | const withIntlAsProp = WrappedComponent => 4 | function WithIntlAsPropComponent(props) { 5 | return {({ intl }) => }; 6 | }; 7 | 8 | export default withIntlAsProp; 9 | -------------------------------------------------------------------------------- /server/.eslintignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | apidoc 3 | jsdoc 4 | node_modules 5 | static 6 | -------------------------------------------------------------------------------- /server/.prettierignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | apidoc 3 | jsdoc 4 | node_modules 5 | static 6 | -------------------------------------------------------------------------------- /server/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "trailingComma": "all", 6 | "arrowParens": "always", 7 | "tabWidth": 2, 8 | "useTabs": false 9 | } 10 | -------------------------------------------------------------------------------- /server/api/controllers/ping.controller.js: -------------------------------------------------------------------------------- 1 | const asyncMiddleware = require('../middlewares/asyncMiddleware'); 2 | 3 | module.exports = function PingController() { 4 | /** 5 | * @api {get} /api/v1/ping ping 6 | * @apiName ping 7 | * @apiGroup Ping 8 | */ 9 | async function ping(req, res) { 10 | res.json({ status: 200 }); 11 | } 12 | 13 | return Object.freeze({ 14 | ping: asyncMiddleware(ping), 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /server/api/middlewares/adminMiddleware.js: -------------------------------------------------------------------------------- 1 | const { Error403 } = require('../../utils/httpErrors'); 2 | const { USER_ROLE } = require('../../utils/constants'); 3 | 4 | module.exports = function adminMiddleware(req, res, next) { 5 | if (req.user && req.user.role === USER_ROLE.ADMIN) { 6 | next(); 7 | } else { 8 | throw new Error403('This route is only accessible to admin user.'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /server/api/middlewares/asyncMiddleware.js: -------------------------------------------------------------------------------- 1 | const Promise = require('bluebird'); 2 | 3 | const asyncMiddleware = (fn) => (req, res, next) => { 4 | return Promise.resolve(fn(req, res, next)).catch(next); 5 | }; 6 | 7 | module.exports = asyncMiddleware; 8 | -------------------------------------------------------------------------------- /server/api/middlewares/corsMiddleware.js: -------------------------------------------------------------------------------- 1 | module.exports = function CorsMiddleware(req, res, next) { 2 | res.header('Access-Control-Allow-Origin', '*'); 3 | res.header('Access-Control-Allow-Methods', 'GET,PUT,PATCH,POST,DELETE'); 4 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization'); 5 | if (req.method === 'OPTIONS') { 6 | res.send(); 7 | } else { 8 | next(); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /server/api/middlewares/isInstanceConfigured.js: -------------------------------------------------------------------------------- 1 | const asyncMiddleware = require('./asyncMiddleware'); 2 | const { Error403 } = require('../../utils/httpErrors'); 3 | 4 | module.exports = function IsInstanceConfiguredMiddleware(gladys) { 5 | return asyncMiddleware(async (req, res, next) => { 6 | const numberOfUsers = gladys.user.getUserCount(); 7 | if (numberOfUsers === 0) { 8 | next(); 9 | } else { 10 | throw new Error403('INSTANCE_ALREADY_CONFIGURED'); 11 | } 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /server/api/middlewares/notFoundMiddleware.js: -------------------------------------------------------------------------------- 1 | const { Error404 } = require('../../utils/httpErrors'); 2 | 3 | module.exports = function notFoundMiddleware(req, res, next) { 4 | throw new Error404(`Route ${req.path} not found`); 5 | }; 6 | -------------------------------------------------------------------------------- /server/api/middlewares/rateLimitMiddleware.js: -------------------------------------------------------------------------------- 1 | const rateLimit = require('express-rate-limit'); 2 | 3 | // @ts-ignore 4 | const limiter = rateLimit({ 5 | windowMs: 5 * 60 * 1000, // 5 minutes 6 | max: 100, // limit each IP to 100 requests 7 | }); 8 | 9 | module.exports = limiter; 10 | -------------------------------------------------------------------------------- /server/config/brain/backup/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "backup.fail", 4 | "answers": [ 5 | "The Gladys Plus backup failed. For more information, go to the \"Settings\" => \"Tasks\" tab. The error message is {{ errorMessage }}" 6 | ] 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /server/config/brain/backup/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "backup.fail", 4 | "answers": [ 5 | "La sauvegarde Gladys Plus a échoué. Pour en savoir plus, rendez-vous sur l'onglet \"Paramètres\" => \"Tâches\". Le message d'erreur est {{ errorMessage }}" 6 | ] 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /server/config/brain/battery-threshold/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "battery-threshold.success", 4 | "answers": [ 5 | "Warning ! Battery level on {{ device.name }} is under {{ value.min }}% (current: {{ value.current }}%)" 6 | ] 7 | }, 8 | { 9 | "label": "battery-level-is-low.success", 10 | "answers": ["Warning ! Battery level on {{ device.name }} is low !"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/battery-threshold/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "battery-threshold.success", 4 | "answers": [ 5 | "Avertissement ! Le niveau de la batterie de {{ device.name }} est inférieur à {{ value.min }}% (actuel : {{ value.current }}%)" 6 | ] 7 | }, 8 | { 9 | "label": "battery-level-is-low.success", 10 | "answers": ["Avertissement ! Le niveau de la batterie de {{ device.name }} est faible !"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/calendar/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "calendar.next-event.get-location.success", 4 | "answers": ["Your next event is {{ event.name }} and is located at {{ event.location }}"] 5 | }, 6 | { 7 | "label": "calendar.next-event.get-location.fail", 8 | "answers": ["I was unable to get your next event."] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /server/config/brain/calendar/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "calendar.next-event.get-location.success", 4 | "answers": ["Votre prochain événement est {{ event.name }} et aura lieu à {{ event.location }}"] 5 | }, 6 | { 7 | "label": "calendar.next-event.get-location.fail", 8 | "answers": ["Je n'ai pas réussi à trouver votre prochain évènement."] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /server/config/brain/calendar/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "calendar.next-event.get-location", 4 | "questions": ["Where is my next event?", "Where should I go next?"], 5 | "answers": ["Let me check your calendar..."] 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /server/config/brain/calendar/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "calendar.next-event.get-location", 4 | "questions": ["Quand a lieu mon prochain évènement?", "Ou dois-je aller maintenant?"], 5 | "answers": ["Je regarde votre calendrier..."] 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /server/config/brain/camera/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "camera.get-image.success", 4 | "answers": ["Here is what I see:"] 5 | }, 6 | 7 | { 8 | "label": "camera.get-image.no-image-found", 9 | "answers": ["Sorry, I can't get a recent image."] 10 | }, 11 | { 12 | "label": "camera.get-image.fail", 13 | "answers": ["Sorry, something wrong happened while getting the camera"] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /server/config/brain/camera/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "camera.get-image.success", 4 | "answers": ["Voilà ce que je vois:"] 5 | }, 6 | 7 | { 8 | "label": "camera.get-image.no-image-found", 9 | "answers": ["Désolé, je n'ai pas trouvé d'image récente."] 10 | }, 11 | { 12 | "label": "camera.get-image.fail", 13 | "answers": ["Désolé, je n'ai pas réussi à récupérer cette image de caméra"] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /server/config/brain/camera/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "camera.get-image", 4 | "questions": [ 5 | "Show me a view of the camera in %room%", 6 | "Display camera image in %room%", 7 | "Show me a view of the camera %device%", 8 | "Display camera image of %device%" 9 | ] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /server/config/brain/camera/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "camera.get-image", 4 | "questions": [ 5 | "Montre moi la caméra du %room%", 6 | "Montre moi le %room%", 7 | "Montre moi la caméra de la %room%", 8 | "Montre moi %device%", 9 | "Montre moi la caméra %device%", 10 | "Montre moi %device%" 11 | ] 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /server/config/brain/chat/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "question.no-intent-found", 4 | "answers": ["I didn't understand what you said..."] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/chat/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "question.no-intent-found", 4 | "answers": ["Je n'ai pas compris..."] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/chat/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "chat.greeting", 4 | "questions": ["Hey!"], 5 | "answers": ["Hey!"] 6 | }, 7 | { 8 | "label": "chat.i-love-you", 9 | "questions": ["I love you"], 10 | "answers": ["Me too!", "Of course you love me..."] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/chat/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "chat.greeting", 4 | "questions": ["Hey!"], 5 | "answers": ["Hey!"] 6 | }, 7 | { 8 | "label": "chat.i-love-you", 9 | "questions": ["Je t'aime"], 10 | "answers": ["Moi aussi !", "Je le savais..."] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/humidity-sensor/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "humidity-sensor.get-in-room.success", 4 | "answers": ["The humidity level is {{ humidity }}% in the {{ roomName }}."] 5 | }, 6 | { 7 | "label": "humidity-sensor.get-in-room.fail.no-results", 8 | "answers": ["No humidity values were recorded in the last hour in this room."] 9 | }, 10 | { 11 | "label": "humidity-sensor.get-in-room.fail.room-not-found", 12 | "answers": ["I can't find a room with this name."] 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /server/config/brain/humidity-sensor/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "humidity-sensor.get-in-room", 4 | "questions": ["What's the humidity level in the %room% ?", "Give me the humidity level in the %room%"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/humidity-sensor/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "humidity-sensor.get-in-room", 4 | "questions": ["Quel est le taux d'humidité dans le %room% ?", "Donne moi l'humidité du %room%"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/light/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "light.turn-on", 4 | "questions": ["Turn on the light in the %room%", "I want some light in the %room%"], 5 | "answers": ["Turning on the light...", "I'm turning on the light!"] 6 | }, 7 | { 8 | "label": "light.turn-off", 9 | "questions": ["Turn off the light in the %room%", "Shut down the light in the %room%"], 10 | "answers": ["Turning off the light...", "I'm turning off the light!"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/light/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "light.turn-on", 4 | "questions": ["Allume la lumière dans le %room%", "Allume dans le %room%"], 5 | "answers": ["Allumage en cours...", "J'allume la lumière !"] 6 | }, 7 | { 8 | "label": "light.turn-off", 9 | "questions": ["Eteins la lumière dans le %room%", "Eteins dans le %room%"], 10 | "answers": ["Extinction des feux...", "J'éteins la lumière !"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/openai/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "openai.request.fail", 4 | "answers": ["The request to OpenAI failed, please try again!"] 5 | }, 6 | { 7 | "label": "openai.request.tooManyRequests", 8 | "answers": ["You have made too many requests this month to OpenAI!"] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /server/config/brain/openai/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "openai.request.fail", 4 | "answers": ["La requête vers OpenAI a échoué, merci de réessayer !"] 5 | }, 6 | { 7 | "label": "openai.request.tooManyRequests", 8 | "answers": ["Vous avez fait trop de requêtes ce mois-ci vers OpenAI !"] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /server/config/brain/scene/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "scene.start.success", 4 | "answers": ["Scene started with success!"] 5 | }, 6 | 7 | { 8 | "label": "scene.start.fail", 9 | "answers": ["Failed to start scene."] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /server/config/brain/scene/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "scene.start.success", 4 | "answers": ["Scène lancée avec succès !"] 5 | }, 6 | 7 | { 8 | "label": "scene.start.fail", 9 | "answers": ["Impossible de lancer la scène."] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /server/config/brain/scene/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "scene.start", 4 | "questions": ["Run %scene%", "Launch %scene%"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/scene/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "scene.start", 4 | "questions": ["Lance la scène %scene%", "Lance %scene%", "Exécute la scène %scene%", "Exécute %scene%"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/switch/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "switch.turn-on", 4 | "questions": ["Turn on the switch %device%"], 5 | "answers": ["Turning on the switch...", "I'm turning on the switch!"] 6 | }, 7 | { 8 | "label": "switch.turn-off", 9 | "questions": ["Turn off the switch %device%"], 10 | "answers": ["Turning off the switch...", "I'm turning off the switch!"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/switch/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "switch.turn-on", 4 | "questions": ["Allume la prise %device%"], 5 | "answers": ["Allumage en cours...", "J'allume la prise !"] 6 | }, 7 | { 8 | "label": "switch.turn-off", 9 | "questions": ["Eteins la prise %device%"], 10 | "answers": ["Extinction en cours...", "J'éteins la prise !"] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/config/brain/system/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "gladys-upgraded.success", 4 | "answers": ["Gladys has been upgraded to version {{ gladysVersion }}! ✅"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/system/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "gladys-upgraded.success", 4 | "answers": ["Gladys a été mise à jour à la version {{ gladysVersion }} ! ✅"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/temperature-sensor/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "temperature-sensor.get-in-room", 4 | "questions": ["What's the temperature in the %room% ?", "Give me the temperature in the %room%"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/temperature-sensor/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "temperature-sensor.get-in-room", 4 | "questions": ["Quelle est la température dans le %room% ?", "Donne moi la température du %room%"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/user/answers.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "user.forgot-password.success", 4 | "answers": ["You asked to reset your password. Here is the link:"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/user/answers.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "user.forgot-password.success", 4 | "answers": ["Vous avez demandé à réinitialiser votre mot de passe. Voici le lien :"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /server/config/brain/weather/questions.en.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "weather.get", 4 | "questions": [ 5 | "What's the weather like?", 6 | "Give me the weather for tomorrow", 7 | "What will the weather be like %day% ?", 8 | "weather %day% ?" 9 | ] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /server/config/brain/weather/questions.fr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "weather.get", 4 | "questions": [ 5 | "Donne moi la météo", 6 | "Quel temps fait-il %day% ?", 7 | "Donne moi la météo pour %day%", 8 | "Quel temps fera t'il %day% ?", 9 | "Météo %day% ?" 10 | ] 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /server/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "checkJs": true, 4 | "moduleResolution": "NodeNext", 5 | "lib": ["ES2022"], 6 | "target": "ES2022" 7 | }, 8 | 9 | "exclude": ["node_modules", "**/node_modules/*"] 10 | } 11 | -------------------------------------------------------------------------------- /server/lib/area/area.get.js: -------------------------------------------------------------------------------- 1 | const db = require('../../models'); 2 | 3 | /** 4 | * @description Get areas. 5 | * @returns {Promise} Return area. 6 | * @example 7 | * gladys.area.get(); 8 | */ 9 | async function get() { 10 | const areas = await db.Area.findAll(); 11 | 12 | const plainAreas = areas.map((area) => area.get({ plain: true })); 13 | 14 | return plainAreas; 15 | } 16 | 17 | module.exports = { 18 | get, 19 | }; 20 | -------------------------------------------------------------------------------- /server/lib/area/area.init.js: -------------------------------------------------------------------------------- 1 | const db = require('../../models'); 2 | 3 | /** 4 | * @description Init areas in local RAM. 5 | * @returns {Promise} Resolve. 6 | * @example 7 | * gladys.device.init(); 8 | */ 9 | async function init() { 10 | const areas = await db.Area.findAll(); 11 | this.areas = areas.map((area) => area.get({ plain: true })); 12 | } 13 | 14 | module.exports = { init }; 15 | -------------------------------------------------------------------------------- /server/lib/brain/brain.load.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Train the brain. 3 | * @example 4 | * brain.train(); 5 | */ 6 | async function load() { 7 | this.train(); 8 | } 9 | 10 | module.exports = { 11 | load, 12 | }; 13 | -------------------------------------------------------------------------------- /server/lib/device/device.onPurgeStatesEvent.js: -------------------------------------------------------------------------------- 1 | const { JOB_TYPES } = require('../../utils/constants'); 2 | 3 | /** 4 | * @description It's time to do the daily purge of state events. 5 | * @example 6 | * onPurgeStatesEvent() 7 | */ 8 | async function onPurgeStatesEvent() { 9 | const purgeAllStates = this.job.wrapper(JOB_TYPES.DEVICE_STATES_PURGE, async () => { 10 | await this.purgeStates(); 11 | }); 12 | await purgeAllStates(); 13 | } 14 | 15 | module.exports = { 16 | onPurgeStatesEvent, 17 | }; 18 | -------------------------------------------------------------------------------- /server/lib/gateway/gateway.getEcowattSignals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get ecowatt signals. 3 | * @returns {Promise} Resolve data from ecowatt. 4 | * @example 5 | * const data = await getEcowattSignals(); 6 | */ 7 | async function getEcowattSignals() { 8 | const systemInfos = await this.system.getInfos(); 9 | return this.gladysGatewayClient.getEcowattSignals(systemInfos.gladys_version); 10 | } 11 | 12 | module.exports = { 13 | getEcowattSignals, 14 | }; 15 | -------------------------------------------------------------------------------- /server/lib/gateway/gateway.getEdfTempo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get edf tempo. 3 | * @returns {Promise} Resolve data from RTE. 4 | * @example 5 | * const data = await getEdfTempo(); 6 | */ 7 | async function getEdfTempo() { 8 | const systemInfos = await this.system.getInfos(); 9 | return this.gladysGatewayClient.getEdfTempo(systemInfos.gladys_version); 10 | } 11 | 12 | module.exports = { 13 | getEdfTempo, 14 | }; 15 | -------------------------------------------------------------------------------- /server/lib/gateway/gateway.refreshUserKeys.js: -------------------------------------------------------------------------------- 1 | const { SYSTEM_VARIABLE_NAMES } = require('../../utils/constants'); 2 | /** 3 | * @description Refresh cache of gateway user keys. 4 | * @example 5 | * this.refreshUserKeys(); 6 | */ 7 | async function refreshUserKeys() { 8 | // getting users keys 9 | this.usersKeys = JSON.parse(await this.variable.getValue(SYSTEM_VARIABLE_NAMES.GLADYS_GATEWAY_USERS_KEYS)); 10 | } 11 | 12 | module.exports = { refreshUserKeys }; 13 | -------------------------------------------------------------------------------- /server/lib/gateway/gateway.saveUsersKeys.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Save gateway user keys. 3 | * @param {Array} keys - Array of keys. 4 | * @example 5 | * saveUsersKeys([]); 6 | */ 7 | async function saveUsersKeys(keys) { 8 | await this.variable.setValue('GLADYS_GATEWAY_USERS_KEYS', JSON.stringify(keys)); 9 | } 10 | 11 | module.exports = { 12 | saveUsersKeys, 13 | }; 14 | -------------------------------------------------------------------------------- /server/lib/house/house.isEmpty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @public 3 | * @description Return true or false if house is empty. 4 | * @param {string} selector - Selector of the house. 5 | * @returns {Promise} Resolve with true if house is empty. 6 | * @example 7 | * const empty = await gladys.house.isEmpty('my-house'); 8 | */ 9 | async function isEmpty(selector) { 10 | const usersAtHome = await this.getUsersAtHome(selector); 11 | return usersAtHome.length === 0; 12 | } 13 | 14 | module.exports = { 15 | isEmpty, 16 | }; 17 | -------------------------------------------------------------------------------- /server/lib/http/index.js: -------------------------------------------------------------------------------- 1 | const { request } = require('./http.request'); 2 | 3 | const Http = function Http(system) { 4 | this.system = system; 5 | }; 6 | 7 | Http.prototype.request = request; 8 | 9 | module.exports = Http; 10 | -------------------------------------------------------------------------------- /server/lib/room/room.getAll.js: -------------------------------------------------------------------------------- 1 | const db = require('../../models'); 2 | 3 | /** 4 | * @description Get all rooms. 5 | * @returns {Promise} Resolve with list of rooms. 6 | * @example 7 | * const rooms = await room.getAll(); 8 | */ 9 | async function getAll() { 10 | const rooms = await db.Room.findAll(); 11 | const roomsPlain = rooms.map((room) => room.get({ plain: true })); 12 | return roomsPlain; 13 | } 14 | 15 | module.exports = { 16 | getAll, 17 | }; 18 | -------------------------------------------------------------------------------- /server/lib/room/room.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Init room. 3 | * @example 4 | * room.init(); 5 | */ 6 | async function init() { 7 | const rooms = await this.getAll(); 8 | 9 | rooms.forEach((room) => { 10 | this.brain.addNamedEntity('room', room.id, room.name); 11 | }); 12 | } 13 | 14 | module.exports = { 15 | init, 16 | }; 17 | -------------------------------------------------------------------------------- /server/lib/scheduler/index.js: -------------------------------------------------------------------------------- 1 | const schedule = require('node-schedule'); 2 | 3 | const { scheduleJob } = require('./scheduler.scheduleJob'); 4 | const { cancelJob } = require('./scheduler.cancelJob'); 5 | const { init } = require('./scheduler.init'); 6 | 7 | const Scheduler = function Scheduler(event) { 8 | this.event = event; 9 | this.nodeSchedule = schedule; 10 | }; 11 | 12 | Scheduler.prototype.scheduleJob = scheduleJob; 13 | Scheduler.prototype.cancelJob = cancelJob; 14 | Scheduler.prototype.init = init; 15 | 16 | module.exports = Scheduler; 17 | -------------------------------------------------------------------------------- /server/lib/scheduler/scheduler.cancelJob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Cancel job. 3 | * @param {string} jobName - Job name to cancel. 4 | * @example 5 | * scheduler.cancelJob('my-job'); 6 | */ 7 | function cancelJob(jobName) { 8 | this.nodeSchedule.cancelJob(jobName); 9 | } 10 | 11 | module.exports = { 12 | cancelJob, 13 | }; 14 | -------------------------------------------------------------------------------- /server/lib/scheduler/scheduler.scheduleJob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Schedule new job. 3 | * @param {object | string | Date} rule - Rule to execute the job. 4 | * @param {Function} method - The method to execute. 5 | * @returns {object} The scheduled job. 6 | * @example 7 | * scheduler.scheduleJob({ hour: 12 }, () => console.log('job is running')); 8 | */ 9 | function scheduleJob(rule, method) { 10 | return this.nodeSchedule.scheduleJob(rule, method); 11 | } 12 | 13 | module.exports = { 14 | scheduleJob, 15 | }; 16 | -------------------------------------------------------------------------------- /server/lib/service/service.getAll.js: -------------------------------------------------------------------------------- 1 | const db = require('../../models'); 2 | 3 | /** 4 | * @description Get all services. 5 | * @param {string} [podId] - Id of the pod. 6 | * @returns {Promise} - Resolve with services. 7 | * @example 8 | * service.getAll(null); 9 | */ 10 | async function getAll(podId = null) { 11 | return db.Service.findAll({ 12 | where: { 13 | pod_id: podId, 14 | }, 15 | }); 16 | } 17 | 18 | module.exports = { 19 | getAll, 20 | }; 21 | -------------------------------------------------------------------------------- /server/lib/service/service.getService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @public 3 | * @description Load all services. 4 | * @param {string} name - Name of the service to get. 5 | * @returns {object} Return the service or null if not present. 6 | * @example 7 | * service.getService('telegram'); 8 | */ 9 | function getService(name) { 10 | return this.stateManager.get('service', name); 11 | } 12 | 13 | module.exports = { 14 | getService, 15 | }; 16 | -------------------------------------------------------------------------------- /server/lib/service/service.getServiceById.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @public 3 | * @description Get service by its id. 4 | * @param {string} id - Id of the service to get. 5 | * @returns {object} Return the service or null if not present. 6 | * @example 7 | * service.getServiceById('99dc10bb-14ab-49dc-bd11-f724e98fc97c'); 8 | */ 9 | function getServiceById(id) { 10 | return this.stateManager.get('serviceById', id); 11 | } 12 | 13 | module.exports = { 14 | getServiceById, 15 | }; 16 | -------------------------------------------------------------------------------- /server/lib/service/service.getServices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @public 3 | * @description Return all services. 4 | * @returns {object} Return all services. 5 | * @example 6 | * service.getServices(); 7 | */ 8 | function getServices() { 9 | return this.stateManager.state.service; 10 | } 11 | 12 | module.exports = { 13 | getServices, 14 | }; 15 | -------------------------------------------------------------------------------- /server/lib/system/system.saveLatestGladysVersion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Save latest Gladys Version. 3 | * @param {string} latestGladysVersion - The latest Gladys version. 4 | * @example 5 | * saveLatestGladysVersion('v4.0.0'); 6 | */ 7 | async function saveLatestGladysVersion(latestGladysVersion) { 8 | this.latestGladysVersion = latestGladysVersion; 9 | } 10 | 11 | module.exports = { 12 | saveLatestGladysVersion, 13 | }; 14 | -------------------------------------------------------------------------------- /server/lib/system/system.shutdown.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../utils/logger'); 2 | 3 | /** 4 | * @description Shutdown Gladys instance. 5 | * @example 6 | * shutdown(); 7 | */ 8 | async function shutdown() { 9 | // gracefully shutdown db 10 | try { 11 | await this.sequelize.close(); 12 | } catch (e) { 13 | logger.info('Database is probably already closed'); 14 | logger.warn(e); 15 | } 16 | // exit 17 | process.exit(); 18 | } 19 | 20 | module.exports = { 21 | shutdown, 22 | }; 23 | -------------------------------------------------------------------------------- /server/lib/user/user.getUserCount.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Return the number of users in this instance. 3 | * @returns {number} Return the number of users. 4 | * @example 5 | * const nbOfUser = user.getUserCount(); 6 | */ 7 | function getUserCount() { 8 | return Object.keys(this.stateManager.state.user).length; 9 | } 10 | 11 | module.exports = { 12 | getUserCount, 13 | }; 14 | -------------------------------------------------------------------------------- /server/lib/variable/index.js: -------------------------------------------------------------------------------- 1 | const { destroy } = require('./variable.destroy'); 2 | const { getValue } = require('./variable.getValue'); 3 | const { getVariables } = require('./variable.getVariables'); 4 | const { setValue } = require('./variable.setValue'); 5 | 6 | const Variable = function Variable(event) { 7 | this.event = event; 8 | }; 9 | 10 | Variable.prototype.destroy = destroy; 11 | Variable.prototype.setValue = setValue; 12 | Variable.prototype.getValue = getValue; 13 | Variable.prototype.getVariables = getVariables; 14 | 15 | module.exports = Variable; 16 | -------------------------------------------------------------------------------- /server/lib/weather/weather.error.js: -------------------------------------------------------------------------------- 1 | class NoWeatherFoundError extends Error { 2 | constructor(message) { 3 | super(); 4 | this.message = message; 5 | } 6 | } 7 | module.exports = { 8 | NoWeatherFoundError, 9 | }; 10 | -------------------------------------------------------------------------------- /server/migrations/20200123094438-add-triggers-attribute.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_scene', 'triggers', { 4 | type: Sequelize.JSON, 5 | }); 6 | // delete useless trigger table 7 | await queryInterface.dropTable('t_trigger'); 8 | // delete useless trigger_scene table 9 | await queryInterface.dropTable('t_trigger_scene'); 10 | }, 11 | down: async (queryInterface, Sequelize) => {}, 12 | }; 13 | -------------------------------------------------------------------------------- /server/migrations/20200201125436-add-caldav-data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | return Promise.all([ 4 | queryInterface.addColumn('t_calendar', 'ctag', { 5 | type: Sequelize.STRING, 6 | }), 7 | queryInterface.addColumn('t_calendar', 'sync_token', { 8 | type: Sequelize.STRING, 9 | }), 10 | ]); 11 | }, 12 | 13 | down: async (queryInterface, Sequelize) => {}, 14 | }; 15 | -------------------------------------------------------------------------------- /server/migrations/20200207214849-add-calendar-event-url.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | return Promise.all([ 4 | queryInterface.addColumn('t_calendar_event', 'url', { 5 | type: Sequelize.STRING, 6 | }), 7 | ]); 8 | }, 9 | 10 | down: async (queryInterface, Sequelize) => {}, 11 | }; 12 | -------------------------------------------------------------------------------- /server/migrations/20200513195013-session-with-useragent.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_session', 'useragent', { 4 | type: Sequelize.TEXT, 5 | }); 6 | }, 7 | down: async (queryInterface, Sequelize) => {}, 8 | }; 9 | -------------------------------------------------------------------------------- /server/migrations/20201018084535-service-with-status.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_service', 'status', { 4 | allowNull: false, 5 | type: Sequelize.TEXT, 6 | defaultValue: 'UNKNWON', 7 | after: 'enabled', 8 | }); 9 | }, 10 | down: async (queryInterface, Sequelize) => {}, 11 | }; 12 | -------------------------------------------------------------------------------- /server/migrations/20201031091221-service-reword-status.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.sequelize.query("UPDATE t_service SET status='UNKNOWN' WHERE status='UNKNWON'"); 4 | await queryInterface.sequelize.query("UPDATE t_service SET status='RUNNING' WHERE status='READY'"); 5 | }, 6 | down: async (queryInterface, Sequelize) => {}, 7 | }; 8 | -------------------------------------------------------------------------------- /server/migrations/20201128203008-add-color-caldav.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: (queryInterface, Sequelize) => { 3 | return Promise.all([ 4 | queryInterface.addColumn('t_calendar', 'color', { 5 | type: Sequelize.STRING, 6 | }), 7 | ]); 8 | }, 9 | 10 | down: async (queryInterface, Sequelize) => {}, 11 | }; 12 | -------------------------------------------------------------------------------- /server/migrations/20201220100508-service-remove-not-configured-status.js: -------------------------------------------------------------------------------- 1 | const db = require('../models'); 2 | 3 | module.exports = { 4 | up: async () => { 5 | await db.Service.update( 6 | { 7 | status: 'RUNNING', 8 | }, 9 | { 10 | where: { 11 | status: ['NOT_CONFIGURED', 'STOPPED'], 12 | }, 13 | }, 14 | ); 15 | }, 16 | down: async () => {}, 17 | }; 18 | -------------------------------------------------------------------------------- /server/migrations/20210705025615-add-active-in-scene.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_scene', 'active', { 4 | type: Sequelize.BOOLEAN, 5 | defaultValue: true, 6 | allowNull: false, 7 | }); 8 | }, 9 | down: async () => {}, 10 | }; 11 | -------------------------------------------------------------------------------- /server/migrations/20211023172755-share-calendars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_calendar', 'shared', { 4 | type: Sequelize.BOOLEAN, 5 | allowNull: false, 6 | defaultValue: false, 7 | }); 8 | }, 9 | down: async (queryInterface, Sequelize) => {}, 10 | }; 11 | -------------------------------------------------------------------------------- /server/migrations/20211204080815-clean-nan-device-states.js: -------------------------------------------------------------------------------- 1 | const db = require('../models'); 2 | 3 | module.exports = { 4 | up: async () => { 5 | await db.DeviceFeatureState.destroy({ 6 | where: { 7 | value: Number.NaN, 8 | }, 9 | }); 10 | }, 11 | down: async () => {}, 12 | }; 13 | -------------------------------------------------------------------------------- /server/migrations/20220322170955-clean-nan-device-states-again.js: -------------------------------------------------------------------------------- 1 | const db = require('../models'); 2 | 3 | module.exports = { 4 | up: async () => { 5 | await db.DeviceFeatureState.destroy({ 6 | where: { 7 | value: Number.NaN, 8 | }, 9 | }); 10 | }, 11 | down: async () => {}, 12 | }; 13 | -------------------------------------------------------------------------------- /server/migrations/20220503155531-clean-nan-device-states-aggregate.js: -------------------------------------------------------------------------------- 1 | const db = require('../models'); 2 | 3 | module.exports = { 4 | up: async () => { 5 | await db.DeviceFeatureStateAggregate.destroy({ 6 | where: { 7 | value: Number.NaN, 8 | }, 9 | }); 10 | }, 11 | down: async () => {}, 12 | }; 13 | -------------------------------------------------------------------------------- /server/migrations/20220912140232-add-index-device-feature-state.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | // This will help dashboard display queries 4 | await queryInterface.addIndex('t_device_feature_state', ['device_feature_id', 'created_at']); 5 | await queryInterface.addIndex('t_device_feature_state_aggregate', ['device_feature_id', 'type', 'created_at']); 6 | }, 7 | down: async () => {}, 8 | }; 9 | -------------------------------------------------------------------------------- /server/migrations/20230130044921-add-position-dashboard.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_dashboard', 'position', { 4 | type: Sequelize.INTEGER, 5 | allowNull: false, 6 | defaultValue: 0, 7 | }); 8 | }, 9 | down: async (queryInterface, Sequelize) => {}, 10 | }; 11 | -------------------------------------------------------------------------------- /server/migrations/20230511161620-description-scene.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_scene', 'description', { 4 | type: Sequelize.STRING, 5 | }); 6 | }, 7 | down: async (queryInterface, Sequelize) => {}, 8 | }; 9 | -------------------------------------------------------------------------------- /server/migrations/20240513141123-dashboard-visibility.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | up: async (queryInterface, Sequelize) => { 3 | await queryInterface.addColumn('t_dashboard', 'visibility', { 4 | type: Sequelize.STRING, 5 | defaultValue: 'private', 6 | allowNull: false, 7 | }); 8 | }, 9 | down: () => {}, 10 | }; 11 | -------------------------------------------------------------------------------- /server/services/airplay/lib/airplay.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Scan the network to find IP addresses of Airplay. 3 | * @example init(); 4 | */ 5 | async function init() { 6 | await this.scan(); 7 | } 8 | 9 | module.exports = { 10 | init, 11 | }; 12 | -------------------------------------------------------------------------------- /server/services/airplay/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-airplay", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bonjour": "^3.5.0", 17 | "node-airtunes2": "^2.4.9" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/alexa/lib/alexa.constants.js: -------------------------------------------------------------------------------- 1 | const DIRECTIVE_NAMESPACES = { 2 | PowerController: 'Alexa.PowerController', 3 | BrightnessController: 'Alexa.BrightnessController', 4 | ColorController: 'Alexa.ColorController', 5 | }; 6 | 7 | const DIRECTIVE_NAMESPACES_LIST = Object.values(DIRECTIVE_NAMESPACES); 8 | 9 | module.exports = { 10 | DIRECTIVE_NAMESPACES, 11 | DIRECTIVE_NAMESPACES_LIST, 12 | }; 13 | -------------------------------------------------------------------------------- /server/services/alexa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-alexa", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "freebsd", 9 | "win32" 10 | ], 11 | "cpu": [ 12 | "x64", 13 | "arm", 14 | "arm64" 15 | ], 16 | "scripts": {}, 17 | "dependencies": { 18 | "get-value": "^3.0.1", 19 | "uuid": "^9.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/services/bluetooth/lib/commands/bluetooth.getDiscoveredDevice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Return asked Peripheral, or undefined. 3 | * @param {string} uuid - Wanted peripheral UUID. 4 | * @returns {object} Returns peripheral according to this UUID. 5 | * @example 6 | * const device = bluetoothManager.getDiscoveredDevice('99dd77cba4'); 7 | */ 8 | function getDiscoveredDevice(uuid) { 9 | return this.completeDevice(this.discoveredDevices[uuid]); 10 | } 11 | 12 | module.exports = { 13 | getDiscoveredDevice, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/bluetooth/lib/commands/bluetooth.getDiscoveredDevices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get all found preipherals as devices. 3 | * @returns {object} Return list of devices. 4 | * @example 5 | * const devices = bluetoothManager.getDiscoveredDevices(); 6 | */ 7 | function getDiscoveredDevices() { 8 | return Object.values(this.discoveredDevices).map((device) => this.completeDevice(device)); 9 | } 10 | 11 | module.exports = { 12 | getDiscoveredDevices, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/bluetooth/lib/commands/bluetooth.getStatus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Retrieve current Bluetooth status. 3 | * @returns {object} Return status. 4 | * @example 5 | * bluetooth.getStatus(); 6 | */ 7 | function getStatus() { 8 | const { ready, scanning, peripheralLookup, bluetooth } = this; 9 | const status = { 10 | ready: bluetooth !== undefined && ready, 11 | scanning, 12 | peripheralLookup, 13 | }; 14 | return status; 15 | } 16 | 17 | module.exports = { 18 | getStatus, 19 | }; 20 | -------------------------------------------------------------------------------- /server/services/bluetooth/lib/config/bluetooth.getConfiguration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get Bluetooth configuration. 3 | * @returns {object} - Configuration. 4 | * @example 5 | * this.getConfiguration(); 6 | */ 7 | function getConfiguration() { 8 | const { frequency, status } = this.presenceScanner; 9 | return { 10 | presenceScanner: { frequency, status }, 11 | }; 12 | } 13 | 14 | module.exports = { 15 | getConfiguration, 16 | }; 17 | -------------------------------------------------------------------------------- /server/services/bluetooth/lib/events/bluetooth.scanStart.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../../utils/logger'); 2 | 3 | /** 4 | * @description When the Bluetooth starts scanning. 5 | * @example 6 | * bluetooth.on('startScan', this.scanStart); 7 | */ 8 | function scanStart() { 9 | logger.debug(`Bluetooth: start scanning`); 10 | this.scanning = true; 11 | 12 | this.broadcastStatus(); 13 | } 14 | 15 | module.exports = { 16 | scanStart, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/bluetooth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-bluetooth", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "freebsd", 9 | "win32" 10 | ], 11 | "cpu": [ 12 | "x64", 13 | "arm", 14 | "arm64" 15 | ], 16 | "scripts": {}, 17 | "dependencies": { 18 | "@abandonware/noble": "^1.9.2-26", 19 | "bluebird": "^3.7.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/services/broadlink/lib/commands/broadlink.getPeripherals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Returns stored peripherals. 3 | * @returns {Promise} Discovered peripherals. 4 | * @example 5 | * await gladys.broadlink.getPeripherals(); 6 | */ 7 | async function getPeripherals() { 8 | await this.init(); 9 | return Object.values(this.peripherals); 10 | } 11 | 12 | module.exports = { 13 | getPeripherals, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/broadlink/lib/commands/broadlink.init.js: -------------------------------------------------------------------------------- 1 | const Promise = require('bluebird'); 2 | 3 | /** 4 | * @description Subscribe to Broadlink events. 5 | * @returns {Promise} Null. 6 | * @example 7 | * await gladys.broadlink.init(); 8 | */ 9 | async function init() { 10 | const devices = await this.broadlink.discover(); 11 | await Promise.each(devices, (device) => this.addPeripheral(device)); 12 | return null; 13 | } 14 | 15 | module.exports = { 16 | init, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/broadlink/lib/commands/broadlink.stop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Unsubscribe to Broadlink events. 3 | * @returns {null} Returns when stopped. 4 | * @example 5 | * gladys.broadlink.stop(); 6 | */ 7 | function stop() { 8 | // Stop discovering 9 | this.broadlinkDevices = {}; 10 | this.peripherals = {}; 11 | 12 | Object.values(this.learnTimers).forEach((timer) => clearTimeout(timer)); 13 | this.learnTimers = {}; 14 | 15 | return null; 16 | } 17 | 18 | module.exports = { 19 | stop, 20 | }; 21 | -------------------------------------------------------------------------------- /server/services/broadlink/lib/commands/features/index.js: -------------------------------------------------------------------------------- 1 | const lightDevices = require('./broadlink.light'); 2 | const remoteDevices = require('./broadlink.remote'); 3 | const sensorDevices = require('./broadlink.sensor'); 4 | const switchDevices = require('./broadlink.switch'); 5 | 6 | module.exports = { 7 | DEVICE_MAPPERS: [lightDevices, remoteDevices, sensorDevices, switchDevices], 8 | }; 9 | -------------------------------------------------------------------------------- /server/services/broadlink/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-broadlink", 3 | "version": "1.0.0", 4 | "author": "Alexandre Trovato", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bluebird": "^3.7.2", 17 | "node-broadlink": "^2.4.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/caldav/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-caldav", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "bluebird": "^3.7.0", 17 | "dav-request": "^1.8.0", 18 | "dayjs": "^1.11.10", 19 | "get-value": "^3.0.1", 20 | "ical": "^0.8.0", 21 | "xmldom": "^0.6.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/services/callmebot/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-callmebot", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "gladys-callmebot", 8 | "cpu": [ 9 | "x64", 10 | "arm", 11 | "arm64" 12 | ], 13 | "os": [ 14 | "darwin", 15 | "linux", 16 | "win32" 17 | ] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/callmebot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-callmebot", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {} 15 | } 16 | -------------------------------------------------------------------------------- /server/services/ecowatt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-ecowatt", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "dayjs": "^1.11.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/edf-tempo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-edf-tempo", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "axios": "^1.6.8", 17 | "dayjs": "^1.11.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/enedis/lib/index.js: -------------------------------------------------------------------------------- 1 | const { init } = require('./enedis.init'); 2 | const { sync } = require('./enedis.sync'); 3 | 4 | const EnedisHandler = function EnedisHandler(gladys, serviceId) { 5 | this.gladys = gladys; 6 | this.serviceId = serviceId; 7 | this.syncDelayBetweenCallsInMs = 500; 8 | }; 9 | 10 | EnedisHandler.prototype.init = init; 11 | EnedisHandler.prototype.sync = sync; 12 | 13 | module.exports = EnedisHandler; 14 | -------------------------------------------------------------------------------- /server/services/enedis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-enedis", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "bluebird": "^3.7.0", 17 | "dayjs": "^1.11.5", 18 | "get-value": "^3.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/enedis/utils/parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Return the usage point id of an enedis device. 3 | * @param {string} externalId - External ID of the device. 4 | * @returns {string} Returns the usage point id. 5 | * @example 6 | * const usagePointId = getUsagePointIdFromExternalId('enedis:1234232323'); 7 | */ 8 | function getUsagePointIdFromExternalId(externalId) { 9 | return externalId.split(':')[1]; 10 | } 11 | 12 | module.exports = { 13 | getUsagePointIdFromExternalId, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/ewelink/lib/device/status.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get eWeLink status. 3 | * @returns {object} Current eWeLink network status. 4 | * @example 5 | * status(); 6 | */ 7 | function status() { 8 | const eweLinkStatus = { 9 | configured: this.configured, 10 | connected: this.connected, 11 | }; 12 | return eweLinkStatus; 13 | } 14 | 15 | module.exports = { 16 | status, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/ewelink/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-ewelink", 3 | "version": "1.0.1", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bluebird": "^3.7.2", 17 | "ewelink-api": "^3.1.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/example/lib/light/light.turnOff.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @private 3 | * @description Turn on the light. 4 | * @param {object} deviceFeature - The deviceFeature we wants to control. 5 | * @returns {Promise} Promise. 6 | * @example 7 | * turnOff(deviceFeature); 8 | */ 9 | function turnOff(deviceFeature) { 10 | return this.client.post(`https://some-api/${deviceFeature.external_id}/0`); 11 | } 12 | 13 | module.exports = turnOff; 14 | -------------------------------------------------------------------------------- /server/services/example/lib/light/light.turnOn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @private 3 | * @description Turn on the light. 4 | * @param {object} deviceFeature - The deviceFeature we wants to control. 5 | * @returns {Promise} Promise. 6 | * @example 7 | * turnOn(deviceFeature); 8 | */ 9 | function turnOn(deviceFeature) { 10 | return this.client.post(`https://some-api/${deviceFeature.external_id}/1`); 11 | } 12 | 13 | module.exports = turnOn; 14 | -------------------------------------------------------------------------------- /server/services/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-example", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "axios": "^0.21.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/free-mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-free-mobile", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "axios": "^1.4.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/google-actions/lib/deviceTypes/googleActions.curtain.type.js: -------------------------------------------------------------------------------- 1 | const { DEVICE_FEATURE_CATEGORIES } = require('../../../../utils/constants'); 2 | 3 | /** 4 | * @see https://developers.google.com/assistant/smarthome/guides/curtain 5 | */ 6 | const curtainType = { 7 | key: 'action.devices.types.CURTAIN', 8 | category: DEVICE_FEATURE_CATEGORIES.CURTAIN, 9 | }; 10 | 11 | module.exports = { 12 | curtainType, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/google-actions/lib/deviceTypes/googleActions.light.type.js: -------------------------------------------------------------------------------- 1 | const { DEVICE_FEATURE_CATEGORIES } = require('../../../../utils/constants'); 2 | 3 | /** 4 | * @see https://developers.google.com/assistant/smarthome/guides/light 5 | */ 6 | const lightType = { 7 | key: 'action.devices.types.LIGHT', 8 | category: DEVICE_FEATURE_CATEGORIES.LIGHT, 9 | }; 10 | 11 | module.exports = { 12 | lightType, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/google-actions/lib/deviceTypes/googleActions.shutter.type.js: -------------------------------------------------------------------------------- 1 | const { DEVICE_FEATURE_CATEGORIES } = require('../../../../utils/constants'); 2 | 3 | /** 4 | * @see https://developers.google.com/assistant/smarthome/guides/shutter 5 | */ 6 | const shutterType = { 7 | key: 'action.devices.types.SHUTTER', 8 | category: DEVICE_FEATURE_CATEGORIES.SHUTTER, 9 | }; 10 | 11 | module.exports = { 12 | shutterType, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/google-actions/lib/deviceTypes/googleActions.switch.type.js: -------------------------------------------------------------------------------- 1 | const { DEVICE_FEATURE_CATEGORIES } = require('../../../../utils/constants'); 2 | 3 | /** 4 | * @see https://developers.google.com/assistant/smarthome/guides/switch 5 | */ 6 | const switchType = { 7 | key: 'action.devices.types.SWITCH', 8 | category: DEVICE_FEATURE_CATEGORIES.SWITCH, 9 | }; 10 | 11 | module.exports = { 12 | switchType, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/google-actions/lib/deviceTypes/index.js: -------------------------------------------------------------------------------- 1 | const { curtainType } = require('./googleActions.curtain.type'); 2 | const { lightType } = require('./googleActions.light.type'); 3 | const { shutterType } = require('./googleActions.shutter.type'); 4 | const { switchType } = require('./googleActions.switch.type'); 5 | 6 | module.exports = [curtainType, lightType, shutterType, switchType]; 7 | -------------------------------------------------------------------------------- /server/services/google-actions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-actions", 3 | "version": "1.0.0", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "dependencies": { 15 | "bluebird": "^3.7.2", 16 | "set-value": "^4.0.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/google-cast/lib/google_cast.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Scan the network to find IP addresses of Google Cast. 3 | * @example init(); 4 | */ 5 | async function init() { 6 | await this.scan(); 7 | } 8 | 9 | module.exports = { 10 | init, 11 | }; 12 | -------------------------------------------------------------------------------- /server/services/google-cast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-google-cast", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bonjour": "^3.5.0", 17 | "castv2-client": "^1.2.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/homekit/lib/newPinCode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Generate new pincode. 3 | * @returns {Promise} Resolving with new pin code. 4 | * @example 5 | * newPinCode() 6 | */ 7 | async function newPinCode() { 8 | const rd = () => Math.floor(Math.random() * 10); 9 | const pincode = `${rd()}${rd()}${rd()}-${rd()}${rd()}-${rd()}${rd()}${rd()}`; 10 | 11 | await this.gladys.variable.setValue('HOMEKIT_PIN_CODE', pincode, this.serviceId); 12 | 13 | return pincode; 14 | } 15 | 16 | module.exports = { 17 | newPinCode, 18 | }; 19 | -------------------------------------------------------------------------------- /server/services/homekit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-homekit", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "hap-nodejs": "^0.12.3", 17 | "uuid": "^9.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/lan-manager/lib/lan-manager.getConfiguration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get LANManager configuration. 3 | * @returns {object} - Configuration. 4 | * @example 5 | * this.getConfiguration(); 6 | */ 7 | function getConfiguration() { 8 | const { frequency, status } = this.presenceScanner; 9 | return { 10 | presenceScanner: { frequency, status }, 11 | ipMasks: this.ipMasks, 12 | }; 13 | } 14 | 15 | module.exports = { 16 | getConfiguration, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/lan-manager/lib/lan-manager.getStatus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get service status. 3 | * @returns {object} The service status. 4 | * @example 5 | * const status = lanManager.getStatus(); 6 | */ 7 | function getStatus() { 8 | return { 9 | scanning: this.scanning, 10 | configured: this.configured, 11 | }; 12 | } 13 | 14 | module.exports = { 15 | getStatus, 16 | }; 17 | -------------------------------------------------------------------------------- /server/services/lan-manager/lib/lan-manager.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Initialize LAN manager. 3 | * @example 4 | * await lanManager.init(); 5 | */ 6 | async function init() { 7 | // Load saved config 8 | await this.loadConfiguration(); 9 | // Start scanner presence 10 | this.initPresenceScanner(); 11 | } 12 | 13 | module.exports = { 14 | init, 15 | }; 16 | -------------------------------------------------------------------------------- /server/services/lan-manager/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-lan-manager", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "freebsd", 9 | "win32" 10 | ], 11 | "cpu": [ 12 | "x64", 13 | "arm", 14 | "arm64" 15 | ], 16 | "scripts": {}, 17 | "dependencies": { 18 | "node-sudo-nmap": "^4.0.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/matter/lib/matter.getDevices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get the devices. 3 | * @returns {Array} The devices. 4 | * @example 5 | * const devices = matterHandler.getDevices(); 6 | */ 7 | function getDevices() { 8 | return this.devices; 9 | } 10 | 11 | module.exports = { 12 | getDevices, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/matter/lib/matter.refreshDevices.js: -------------------------------------------------------------------------------- 1 | const Promise = require('bluebird'); 2 | 3 | /** 4 | * @description Refresh the devices. 5 | * @example matter.refreshDevices(); 6 | */ 7 | async function refreshDevices() { 8 | // Reset memory 9 | this.devices = []; 10 | const nodeDetails = this.commissioningController.getCommissionedNodesDetails(); 11 | 12 | await Promise.each(nodeDetails, async (nodeDetail) => { 13 | await this.handleNode(nodeDetail); 14 | }); 15 | } 16 | 17 | module.exports = { 18 | refreshDevices, 19 | }; 20 | -------------------------------------------------------------------------------- /server/services/matter/lib/matter.stop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This will stop the Matter service. 3 | * @example matter.stop(); 4 | */ 5 | async function stop() { 6 | if (this.commissioningController) { 7 | await this.commissioningController.close(); 8 | } 9 | } 10 | 11 | module.exports = { 12 | stop, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/matter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-matter", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "dependencies": { 15 | "@matter/main": "^0.13.0", 16 | "@project-chip/matter.js": "^0.13.0", 17 | "bluebird": "^3.7.2", 18 | "fs-extra": "^11.3.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/matter/utils/constants.js: -------------------------------------------------------------------------------- 1 | const VARIABLES = { 2 | MATTER_ENABLED: 'MATTER_ENABLED', 3 | MATTER_BACKUP: 'MATTER_BACKUP', 4 | }; 5 | 6 | module.exports = { 7 | VARIABLES, 8 | }; 9 | -------------------------------------------------------------------------------- /server/services/melcloud/lib/melcloud.disconnect.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../utils/logger'); 2 | const { STATUS } = require('./utils/melcloud.constants'); 3 | 4 | /** 5 | * @description Disconnects service and dependencies. 6 | * @example 7 | * disconnect(); 8 | */ 9 | function disconnect() { 10 | logger.debug('Disconnecting from MELCLoud...'); 11 | this.contextKey = null; 12 | this.status = STATUS.NOT_INITIALIZED; 13 | } 14 | 15 | module.exports = { 16 | disconnect, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/melcloud/lib/melcloud.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Initialize service with properties and connect to devices. 3 | * @example 4 | * await init(); 5 | */ 6 | async function init() { 7 | const configuration = await this.getConfiguration(); 8 | await this.connect(configuration); 9 | } 10 | 11 | module.exports = { 12 | init, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/melcloud/lib/utils/melcloud.constants.js: -------------------------------------------------------------------------------- 1 | const GLADYS_VARIABLES = { 2 | USERNAME: 'MELCLOUD_USERNAME', 3 | PASSWORD: 'MELCLOUD_PASSWORD', 4 | }; 5 | 6 | const MELCLOUD_ENDPOINT = 'https://app.melcloud.com/Mitsubishi.Wifi.Client/'; 7 | 8 | const STATUS = { 9 | NOT_INITIALIZED: 'not_initialized', 10 | CONNECTING: 'connecting', 11 | CONNECTED: 'connected', 12 | ERROR: 'error', 13 | DISCOVERING_DEVICES: 'discovering', 14 | }; 15 | 16 | module.exports = { 17 | GLADYS_VARIABLES, 18 | MELCLOUD_ENDPOINT, 19 | STATUS, 20 | }; 21 | -------------------------------------------------------------------------------- /server/services/melcloud/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-melcloud", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "axios": "^1.4.0", 17 | "get-value": "^3.0.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/mqtt/lib/checkDockerNetwork.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Check if Gladys network on Docker is well configured for embedded broker. 3 | * @returns {Promise} Promise with true if network is usable. 4 | * @example 5 | * checkDockerNetwork(); 6 | */ 7 | async function checkDockerNetwork() { 8 | const gladysNetworkMode = await this.gladys.system.getNetworkMode(); 9 | return gladysNetworkMode === 'host'; 10 | } 11 | 12 | module.exports = { 13 | checkDockerNetwork, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/mqtt/lib/status.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get MQTT status. 3 | * @returns {object} Current MQTT network status. 4 | * @example 5 | * status(); 6 | */ 7 | function status() { 8 | const mqttStatus = { 9 | configured: this.configured, 10 | connected: this.connected, 11 | }; 12 | return mqttStatus; 13 | } 14 | 15 | module.exports = { 16 | status, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/mqtt/lib/unsubscribe.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../utils/logger'); 2 | 3 | /** 4 | * @description Unsubscribes to a topic. 5 | * @param {string} topic - Topic to unsubscribe to. 6 | * @example 7 | * unsubscribe('topic/to/unsubscribe'); 8 | */ 9 | function unsubscribe(topic) { 10 | logger.info(`Unsubscribing to MQTT topic ${topic}`); 11 | if (this.mqttClient) { 12 | this.mqttClient.unsubscribe(topic); 13 | } 14 | delete this.topicBinds[topic]; 15 | } 16 | 17 | module.exports = { 18 | unsubscribe, 19 | }; 20 | -------------------------------------------------------------------------------- /server/services/mqtt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-mqtt", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "dependencies": { 15 | "get-value": "^3.0.1", 16 | "lodash.clonedeep": "^4.5.0", 17 | "mqtt": "^5.10.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/netatmo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-underscore-dangle": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /server/services/netatmo/lib/netatmo.getStatus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get Netatmo status. 3 | * @returns {object} Current Netatmo network status. 4 | * @example 5 | * status(); 6 | */ 7 | function getStatus() { 8 | const netatmoStatus = { 9 | configured: this.configured, 10 | connected: this.connected, 11 | status: this.status, 12 | }; 13 | return netatmoStatus; 14 | } 15 | 16 | module.exports = { 17 | getStatus, 18 | }; 19 | -------------------------------------------------------------------------------- /server/services/netatmo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-netatmo", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bluebird": "^3.7.2", 17 | "undici": "^7.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/nextcloud-talk/lib/message/message.disconnect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Disconnect Nextcloud Talk. 3 | * @returns {Promise} - Resolve. 4 | * @example 5 | * disconnect(); 6 | */ 7 | async function disconnect() { 8 | Object.keys(this.bots).forEach((userId) => this.stopPolling(userId)); 9 | if (this.abortController) { 10 | this.abortController.abort(); 11 | } 12 | } 13 | 14 | module.exports = { 15 | disconnect, 16 | }; 17 | -------------------------------------------------------------------------------- /server/services/nextcloud-talk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-nextcloud-talk", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "axios": "^1.2.1", 17 | "get-value": "^3.0.1", 18 | "uuid": "^8.3.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/node-red/lib/isEnabled.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Checks if Node-RED is ready to use. 3 | * @returns {boolean} Is the Node-RED environment ready to use? 4 | * @example 5 | * await nodeRed.isEnabled(); 6 | */ 7 | async function isEnabled() { 8 | const nodeRedEnabled = await this.gladys.variable.getValue('NODERED_ENABLED', this.serviceId); 9 | if (nodeRedEnabled === '1') { 10 | return true; 11 | } 12 | return false; 13 | } 14 | 15 | module.exports = { 16 | isEnabled, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/node-red/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-node-red", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "lodash.clonedeep": "^4.5.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/openweather/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-openweather", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "axios": "^0.21.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/philips-hue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-hue", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bluebird": "^3.7.2", 17 | "bottleneck": "^2.19.5", 18 | "node-hue-api": "^4.0.11" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/rtsp-camera/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-rtsp-camera", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bluebird": "^3.7.2", 17 | "bottleneck": "^2.19.5", 18 | "fs-extra": "^8.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/sonos/lib/sonos.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This will init the Sonos library. 3 | * @example sonos.init(); 4 | */ 5 | async function init() { 6 | const { SonosManager } = this.sonosLib; 7 | this.manager = new SonosManager(); 8 | await this.scan(); 9 | } 10 | 11 | module.exports = { 12 | init, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/sonos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-sonos", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "@svrooij/sonos": "^2.5.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/features/modules.js: -------------------------------------------------------------------------------- 1 | const LIGHT_MODULES = [9, 11, 26, 37]; 2 | 3 | module.exports = { 4 | LIGHT_MODULES, 5 | }; 6 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/http/tasmota.http.connect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Initialize service with dependencies and connect to devices. 3 | * @example 4 | * connect(); 5 | */ 6 | function connect() { 7 | // NO-OP 8 | } 9 | 10 | module.exports = { 11 | connect, 12 | }; 13 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/http/tasmota.http.constants.js: -------------------------------------------------------------------------------- 1 | const DEVICE_PARAM_NAME = { 2 | USERNAME: 'username', 3 | PASSWORD: 'password', 4 | }; 5 | 6 | module.exports = { 7 | DEVICE_PARAM_NAME, 8 | }; 9 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/http/tasmota.http.disconnect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Disconnect service from dependencies. 3 | * @example 4 | * disconnect(); 5 | */ 6 | function disconnect() { 7 | // NO-OP 8 | } 9 | 10 | module.exports = { 11 | disconnect, 12 | }; 13 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/http/tasmota.http.getDiscoveredDevices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get all discovered devices. 3 | * @returns {*} Discovered devices. 4 | * @example 5 | * getDiscoveredDevices(); 6 | */ 7 | function getDiscoveredDevices() { 8 | return this.discoveredDevices; 9 | } 10 | 11 | module.exports = { 12 | getDiscoveredDevices, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/mqtt/tasmota.mqtt.disconnect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Disconnect service from dependencies. 3 | * @example 4 | * disconnect(); 5 | */ 6 | function disconnect() { 7 | // Unsubscribe to Tasmota topics 8 | this.mqttService.device.unsubscribe('stat/+/+'); 9 | this.mqttService.device.unsubscribe('tele/+/+'); 10 | } 11 | 12 | module.exports = { 13 | disconnect, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/mqtt/tasmota.mqtt.getDiscoveredDevices.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get all discovered devices. 3 | * @returns {*} Discovered devices. 4 | * @example 5 | * getDiscoveredDevices(); 6 | */ 7 | function getDiscoveredDevices() { 8 | return this.discoveredDevices; 9 | } 10 | 11 | module.exports = { 12 | getDiscoveredDevices, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/mqtt/tasmota.mqtt.scan.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Force MQTT scanning by re-subscribing to topic. 3 | * @example 4 | * scan(); 5 | */ 6 | function scan() { 7 | // Subscribe to Tasmota 8 | this.mqttService.device.unsubscribe('tele/+/+'); 9 | this.mqttService.device.subscribe('tele/+/+', this.handleMessage.bind(this)); 10 | } 11 | 12 | module.exports = { 13 | scan, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/tasmota.connect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Initialize service with dependencies and connect to devices. 3 | * @returns {any} NULL. 4 | * @example 5 | * connect(); 6 | */ 7 | function connect() { 8 | Object.values(this.protocols).forEach((handler) => handler.connect()); 9 | return null; 10 | } 11 | 12 | module.exports = { 13 | connect, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/tasmota.constants.js: -------------------------------------------------------------------------------- 1 | const DEVICE_PARAM_NAME = { 2 | PROTOCOL: 'protocol', 3 | USERNAME: 'username', 4 | PASSWORD: 'password', 5 | }; 6 | 7 | const DEVICE_PARAM_VALUE = { 8 | [DEVICE_PARAM_NAME.PROTOCOL]: { 9 | HTTP: 'http', 10 | MQTT: 'mqtt', 11 | }, 12 | }; 13 | 14 | module.exports = { 15 | DEVICE_PARAM_NAME, 16 | DEVICE_PARAM_VALUE, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/tasmota.disconnect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Disconnect service from dependencies. 3 | * @returns {any} NULL. 4 | * @example 5 | * disconnect(); 6 | */ 7 | function disconnect() { 8 | Object.values(this.protocols).forEach((handler) => handler.disconnect()); 9 | return null; 10 | } 11 | 12 | module.exports = { 13 | disconnect, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/tasmota.poll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Polling requested device. 3 | * @param {object} device - Device to poll. 4 | * @example 5 | * tasmotaManager.poll({}), 6 | */ 7 | function poll(device) { 8 | const protocol = this.getProtocolFromDevice(device); 9 | this.getHandler(protocol).getValue(device); 10 | } 11 | 12 | module.exports = { 13 | poll, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/tasmota/lib/tasmota.scan.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Force scanning for devices. 3 | * @param {string} protocol - Protocol to scan over. 4 | * @param {object} options - Options used to scan. 5 | * @example 6 | * scan(['http'], options); 7 | */ 8 | function scan(protocol, options) { 9 | this.getHandler(protocol).scan(options); 10 | } 11 | 12 | module.exports = { 13 | scan, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/tasmota/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-tasmota", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "uuid": "^9.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/telegram/lib/message.disconnect.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../utils/logger'); 2 | 3 | /** 4 | * @description Disconnect telegram API. 5 | * @example 6 | * disconnect(); 7 | */ 8 | async function disconnect() { 9 | logger.debug('Disconnecting Telegram API'); 10 | if (this.bot) { 11 | await this.bot.stopPolling(); 12 | } 13 | this.bot = null; 14 | } 15 | 16 | module.exports = { 17 | disconnect, 18 | }; 19 | -------------------------------------------------------------------------------- /server/services/telegram/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-telegram", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "scripts": {}, 15 | "dependencies": { 16 | "node-telegram-bot-api": "^0.66.0", 17 | "uuid": "^9.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/tp-link/lib/utils/consts.js: -------------------------------------------------------------------------------- 1 | const TP_LINK_EXTERNAL_ID_BASE = 'tp-link'; 2 | const TP_LINK_ON = 1; 3 | const TP_LINK_OFF = 0; 4 | const TP_LINK_IP_ADDRESS = 'TP_LINK_IP_ADDRESS'; 5 | const TP_LINK_SERIAL_NUMBER = 'TP_LINK_SERIAL_NUMBER'; 6 | 7 | module.exports = { 8 | TP_LINK_EXTERNAL_ID_BASE, 9 | TP_LINK_ON, 10 | TP_LINK_OFF, 11 | TP_LINK_IP_ADDRESS, 12 | TP_LINK_SERIAL_NUMBER, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/tp-link/lib/utils/parseExternalId.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Parse the external id and return the plug ID. 3 | * @param {string} externalId - External id of the device. 4 | * @returns {string} Return the deviceId. 5 | * @example 6 | * parseExternalId('tp-link-plug:1'); 7 | */ 8 | function parseExternalId(externalId) { 9 | const parsedExternalId = externalId.split(':'); 10 | const deviceId = parsedExternalId[1]; 11 | return deviceId; 12 | } 13 | 14 | module.exports = { 15 | parseExternalId, 16 | }; 17 | -------------------------------------------------------------------------------- /server/services/tp-link/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-tplink", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "bluebird": "^3.7.2", 17 | "bottleneck": "^2.19.5", 18 | "tplink-smarthome-api": "^3.1.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/services/tuya/lib/tuya.disconnect.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../utils/logger'); 2 | const { STATUS } = require('./utils/tuya.constants'); 3 | 4 | /** 5 | * @description Disconnects service and dependencies. 6 | * @example 7 | * disconnect(); 8 | */ 9 | function disconnect() { 10 | logger.debug('Disonnecting from Tuya...'); 11 | this.connector = null; 12 | this.status = STATUS.NOT_INITIALIZED; 13 | } 14 | 15 | module.exports = { 16 | disconnect, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/tuya/lib/tuya.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Initialize service with properties and connect to devices. 3 | * @example 4 | * await init(); 5 | */ 6 | async function init() { 7 | const configuration = await this.getConfiguration(); 8 | await this.connect(configuration); 9 | } 10 | 11 | module.exports = { 12 | init, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/tuya/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-tuya", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "@tuya/tuya-connector-nodejs": "^2.1.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/services/usb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-usb", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64", 13 | "arm64e" 14 | ], 15 | "scripts": {}, 16 | "dependencies": { 17 | "serialport": "^13.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/xiaomi/lib/commands/xiaomi.getSensors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Return list of sensors. 3 | * @returns {Array} Return array of sensor. 4 | * @example 5 | * xiaomi.getSensors(); 6 | */ 7 | function getSensors() { 8 | return Object.keys(this.sensors).map((sensorId) => this.sensors[sensorId]); 9 | } 10 | 11 | module.exports = { 12 | getSensors, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/xiaomi/lib/event/xiaomi.listening.js: -------------------------------------------------------------------------------- 1 | const DISCOVERY_PORT = 4321; 2 | const MULTICAST_ADDRESS = '224.0.0.50'; 3 | 4 | /** 5 | * @description On listening event. 6 | * @example 7 | * xiaomi.listening(); 8 | */ 9 | function listening() { 10 | this.socket.addMembership(MULTICAST_ADDRESS); 11 | const payload = '{"cmd": "whois"}'; 12 | this.socket.send(payload, 0, payload.length, DISCOVERY_PORT, MULTICAST_ADDRESS); 13 | } 14 | 15 | module.exports = { 16 | listening, 17 | }; 18 | -------------------------------------------------------------------------------- /server/services/xiaomi/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-xiaomi", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "gladys-xiaomi", 8 | "cpu": [ 9 | "x64", 10 | "arm", 11 | "arm64" 12 | ], 13 | "os": [ 14 | "darwin", 15 | "linux" 16 | ] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/xiaomi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-xiaomi", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux" 7 | ], 8 | "cpu": [ 9 | "x64", 10 | "arm", 11 | "arm64" 12 | ], 13 | "scripts": {}, 14 | "dependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/docker/mosquitto.conf: -------------------------------------------------------------------------------- 1 | listener 1884 2 | allow_anonymous false 3 | password_file /mosquitto/config/mosquitto.passwd 4 | 5 | persistence true 6 | persistence_location /mosquitto/config/ -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/exposes/index.js: -------------------------------------------------------------------------------- 1 | const binaryType = require('./binaryType'); 2 | const numericType = require('./numericType'); 3 | const enumType = require('./enumType'); 4 | const compositeType = require('./compositeType'); 5 | 6 | module.exports = { 7 | [binaryType.type]: binaryType, 8 | [numericType.type]: numericType, 9 | [enumType.type]: enumType, 10 | [compositeType.type]: compositeType, 11 | }; 12 | -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/lib/events/emitStatusEvent.js: -------------------------------------------------------------------------------- 1 | const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../../../utils/constants'); 2 | 3 | /** 4 | * @description Emit event with Zigbee2Mqtt status. 5 | * @example 6 | * this.emitStatusEvent(); 7 | */ 8 | function emitStatusEvent() { 9 | const payload = this.status(); 10 | 11 | this.gladys.event.emit(EVENTS.WEBSOCKET.SEND_ALL, { 12 | type: WEBSOCKET_MESSAGE_TYPES.ZIGBEE2MQTT.STATUS_CHANGE, 13 | payload, 14 | }); 15 | } 16 | 17 | module.exports = { 18 | emitStatusEvent, 19 | }; 20 | -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/lib/getManagedAdapters.js: -------------------------------------------------------------------------------- 1 | const { ADAPTERS } = require('../adapters'); 2 | 3 | /** 4 | * @description Get managed adapters. 5 | * @returns {Array} Managed adapters. 6 | * @example 7 | * const adapters = this.getManagedAdapters(); 8 | */ 9 | function getManagedAdapters() { 10 | return ADAPTERS; 11 | } 12 | 13 | module.exports = { 14 | getManagedAdapters, 15 | }; 16 | -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/lib/getPermitJoin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Get permit_join state from Gladys. 3 | * @returns {boolean} Status of Zigbee2mqtt Permit join. 4 | * @example 5 | * init(); 6 | */ 7 | function getPermitJoin() { 8 | return this.z2mPermitJoin; 9 | } 10 | 11 | module.exports = { 12 | getPermitJoin, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/lib/isEnabled.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Checks if z2m is ready to use. 3 | * @returns {boolean} Is the z2m environment ready to use? 4 | * @example 5 | * z2m.isEnabled(); 6 | */ 7 | function isEnabled() { 8 | return this.mqttRunning && this.zigbee2mqttRunning; 9 | } 10 | 11 | module.exports = { 12 | isEnabled, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/zigbee2mqtt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-zigbee2mqtt", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "os": [ 6 | "darwin", 7 | "linux", 8 | "win32" 9 | ], 10 | "cpu": [ 11 | "x64", 12 | "arm", 13 | "arm64" 14 | ], 15 | "dependencies": { 16 | "fs-extra": "^11.1.1", 17 | "jszip": "^3.10.1", 18 | "lodash.clonedeep": "^4.5.0", 19 | "mqtt": "^4.2.6", 20 | "portfinder": "^1.0.32", 21 | "yaml": "^2.2.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/services/zwavejs-ui/lib/zwaveJSUI.getDevice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This will return the Gladys device. 3 | * @param {string} nodeId - The gladys device id. 4 | * @returns {object} The Gladys Device. 5 | * @example zwaveJSUI.getDevice('zwavejs-ui:5'); 6 | */ 7 | function getDevice(nodeId) { 8 | return this.devices.find((n) => n.external_id === nodeId); 9 | } 10 | 11 | module.exports = { 12 | getDevice, 13 | }; 14 | -------------------------------------------------------------------------------- /server/services/zwavejs-ui/lib/zwaveJSUI.getZwaveJsDevice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This will return the zwaveJs device. 3 | * @param {string} gladysDeviceId - The gladys device id. 4 | * @returns {object} The zwaveJsDevice. 5 | * @example zwaveJSUI.getZwaveJsDevice("zwavejs-ui:5"); 6 | */ 7 | function getZwaveJsDevice(gladysDeviceId) { 8 | const deviceId = parseInt(gladysDeviceId.split(':')[1], 10); 9 | 10 | return this.zwaveJSDevices.find((n) => n.id === deviceId); 11 | } 12 | 13 | module.exports = { 14 | getZwaveJsDevice, 15 | }; 16 | -------------------------------------------------------------------------------- /server/services/zwavejs-ui/lib/zwaveJSUI.init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description This will init the Z-Wave JS UI MQTT connection. 3 | * @example zwaveJSUI.init(); 4 | */ 5 | async function init() { 6 | await this.connect(); 7 | } 8 | 9 | module.exports = { 10 | init, 11 | }; 12 | -------------------------------------------------------------------------------- /server/services/zwavejs-ui/lib/zwaveJSUI.scan.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../utils/logger'); 2 | 3 | /** 4 | * @description This will discovery Z-Wave JS UI devices. 5 | * @example zwaveJSUI.scan(); 6 | */ 7 | function scan() { 8 | logger.info('Asking ZWave JS UI for the list of devices'); 9 | this.publish('zwave/_CLIENTS/ZWAVE_GATEWAY-zwave-js-ui/api/getNodes/set', 'true'); 10 | } 11 | 12 | module.exports = { 13 | scan, 14 | }; 15 | -------------------------------------------------------------------------------- /server/services/zwavejs-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gladys-zwavejs-ui", 3 | "main": "index.js", 4 | "os": [ 5 | "darwin", 6 | "linux", 7 | "win32" 8 | ], 9 | "cpu": [ 10 | "x64", 11 | "arm", 12 | "arm64" 13 | ], 14 | "dependencies": { 15 | "bluebird": "^3.7.2", 16 | "get-value": "^3.0.1", 17 | "mqtt": "^5.10.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/services/zwavejs-ui/utils/cleanNames.js: -------------------------------------------------------------------------------- 1 | const cleanNames = (text) => { 2 | if (!text || typeof text !== 'string') { 3 | return ''; 4 | } 5 | return text 6 | .replaceAll(' ', '_') 7 | .replaceAll('(', '') 8 | .replaceAll(')', '') 9 | .toLowerCase(); 10 | }; 11 | 12 | module.exports = cleanNames; 13 | -------------------------------------------------------------------------------- /server/test/controllers/notFound.test.js: -------------------------------------------------------------------------------- 1 | const { request } = require('./request.test'); 2 | 3 | describe('GET /thisroutedoesntexist', () => { 4 | it('should return 404 not found', async () => { 5 | await request.get('/thisroutedoesntexist').expect(404); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /server/test/controllers/ping/ping.controller.test.js: -------------------------------------------------------------------------------- 1 | const { request } = require('../request.test'); 2 | 3 | describe('GET /api/v1/ping', () => { 4 | it('should return ping', async () => { 5 | await request 6 | .get('/api/v1/ping') 7 | .expect('Content-Type', /json/) 8 | .expect(200); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /server/test/controllers/user/user.getSetupState.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { request } = require('../request.test'); 3 | 4 | describe('GET /api/v1/setup', () => { 5 | it('should return if the account is confifured or not', async () => { 6 | await request 7 | .get('/api/v1/setup') 8 | .expect('Content-Type', /json/) 9 | .expect(200) 10 | .then((res) => { 11 | expect(res.body).to.have.property('account_configured', true); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /server/test/lib/gateway/encoded-gladys-db-and-duckdb-backup.tar.gz.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/encoded-gladys-db-and-duckdb-backup.tar.gz.enc -------------------------------------------------------------------------------- /server/test/lib/gateway/encoded-old-gladys-db-backup.db.gz.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/encoded-old-gladys-db-backup.db.gz.enc -------------------------------------------------------------------------------- /server/test/lib/gateway/gladys_backup_parquet_folder/load.sql: -------------------------------------------------------------------------------- 1 | COPY t_device_feature_state FROM 'gladys-backups/gladys-db-backup_2024-6-28-14-57-29_parquet_folder/t_device_feature_state.parquet' (FORMAT 'parquet', COMPRESSION 'GZIP'); 2 | -------------------------------------------------------------------------------- /server/test/lib/gateway/gladys_backup_parquet_folder/schema.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CREATE TABLE t_device_feature_state(device_feature_id UUID, "value" DOUBLE, created_at TIMESTAMP WITH TIME ZONE); 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /server/test/lib/gateway/gladys_backup_parquet_folder/t_device_feature_state.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/gladys_backup_parquet_folder/t_device_feature_state.parquet -------------------------------------------------------------------------------- /server/test/lib/gateway/real-gladys-db-backup.db.gz.dbfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/real-gladys-db-backup.db.gz.dbfile -------------------------------------------------------------------------------- /server/test/lib/gateway/this_db_has_no_users.dbfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/this_db_has_no_users.dbfile -------------------------------------------------------------------------------- /server/test/lib/gateway/this_file_has_no_user_table.dbfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/this_file_has_no_user_table.dbfile -------------------------------------------------------------------------------- /server/test/lib/gateway/this_file_is_not_a_valid_db.dbfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GladysAssistant/Gladys/b59d804c651ec1058e2c0a65c43296a48baab01c/server/test/lib/gateway/this_file_is_not_a_valid_db.dbfile -------------------------------------------------------------------------------- /server/test/lib/http/AxiosMock.test.js: -------------------------------------------------------------------------------- 1 | const { fake } = require('sinon'); 2 | 3 | const axios = { 4 | request: fake.resolves({ data: { success: true }, status: 200, headers: { 'content-type': 'application/json' } }), 5 | }; 6 | 7 | module.exports = axios; 8 | -------------------------------------------------------------------------------- /server/test/lib/service/service.getLocalServiceByName.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | 3 | const Service = require('../../../lib/service'); 4 | 5 | describe('service.getLocalServiceByName', () => { 6 | const service = new Service(); 7 | it('should return test-service', async () => { 8 | const result = await service.getLocalServiceByName('test-service'); 9 | expect(result).to.have.property('name', 'test-service'); 10 | expect(result).to.have.property('id', 'a810b8db-6d04-4697-bed3-c4b72c996279'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /server/test/services/ewelink/mocks/eweLink-2ch.json: -------------------------------------------------------------------------------- 1 | { 2 | "online": true, 3 | "name": "Test 1", 4 | "deviceid": "10004531ae", 5 | "apikey": "validApikey", 6 | "extra": { 7 | "extra": { 8 | "uiid": 7, 9 | "model": "PSA-BHA-GL" 10 | } 11 | }, 12 | "params": { 13 | "fwVersion": "3.3.0" 14 | }, 15 | "ip": "192.168.0.1", 16 | "brandName": "Sonoff", 17 | "productModel": "2CH", 18 | "uiid": 7 19 | } 20 | -------------------------------------------------------------------------------- /server/test/services/ewelink/mocks/eweLink-basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "online": true, 3 | "name": "Test 6", 4 | "deviceid": "10004536ae", 5 | "apikey": "validApikey", 6 | "extra": { 7 | "extra": { 8 | "uiid": 6, 9 | "model": "PSA-BHA-GL" 10 | } 11 | }, 12 | "params": { 13 | "fwVersion": "3.3.0" 14 | }, 15 | "ip": "192.168.0.6", 16 | "brandName": "Sonoff", 17 | "productModel": "Basic", 18 | "uiid": 7 19 | } 20 | -------------------------------------------------------------------------------- /server/test/services/ewelink/mocks/eweLink-offline.json: -------------------------------------------------------------------------------- 1 | { 2 | "online": false, 3 | "name": "Test 2", 4 | "type": "10", 5 | "deviceid": "10004532ae", 6 | "apikey": "validApikey", 7 | "extra": { 8 | "extra": { 9 | "uiid": 1, 10 | "model": "PSF-BD1-GL" 11 | } 12 | }, 13 | "brandName": "SONOFF", 14 | "productModel": "MINI", 15 | "uiid": 1 16 | } 17 | -------------------------------------------------------------------------------- /server/test/services/ewelink/mocks/eweLink-pow.json: -------------------------------------------------------------------------------- 1 | { 2 | "online": true, 3 | "name": "Test 3", 4 | "deviceid": "10004533ae", 5 | "apikey": "validApikey", 6 | "extra": { 7 | "extra": { 8 | "uiid": 5, 9 | "model": "ITA-GZ1-GL" 10 | } 11 | }, 12 | "params": { 13 | "fwVersion": "3.3.0" 14 | }, 15 | "ip": "192.168.0.3", 16 | "brandName": "Sonoff", 17 | "productModel": "Pow", 18 | "uiid": 5 19 | } 20 | -------------------------------------------------------------------------------- /server/test/services/ewelink/mocks/eweLink-th.json: -------------------------------------------------------------------------------- 1 | { 2 | "online": true, 3 | "name": "Test 4", 4 | "deviceid": "10004534ae", 5 | "apikey": "validApikey", 6 | "extra": { 7 | "extra": { 8 | "uiid": 15, 9 | "model": "ITA-GZ1-GL" 10 | } 11 | }, 12 | "params": { 13 | "fwVersion": "3.1.2" 14 | }, 15 | "ip": "192.168.0.4", 16 | "brandName": "Sonoff", 17 | "productModel": "TH", 18 | "uiid": 15 19 | } 20 | -------------------------------------------------------------------------------- /server/test/services/ewelink/mocks/eweLink-unhandled.json: -------------------------------------------------------------------------------- 1 | { 2 | "online": true, 3 | "name": "", 4 | "deviceid": "10004535ae", 5 | "apikey": "validApikey", 6 | "extra": { 7 | "extra": { 8 | "uiid": 10000, 9 | "model": "ITU-GB1-LG" 10 | } 11 | }, 12 | "brandName": "SONOFF", 13 | "productModel": "UNKNOWN", 14 | "uiid": 10000 15 | } 16 | -------------------------------------------------------------------------------- /server/test/services/example/mocks.test.js: -------------------------------------------------------------------------------- 1 | const logger = require('../../../utils/logger'); 2 | 3 | const MockedClient = { 4 | create: function create() { 5 | return { 6 | post: (url) => Promise.resolve(logger.info(`Changing light state, calling ${url}`)), 7 | get: (url) => Promise.resolve(true), 8 | }; 9 | }, 10 | }; 11 | 12 | module.exports = MockedClient; 13 | -------------------------------------------------------------------------------- /server/test/services/google-actions/index.test.js: -------------------------------------------------------------------------------- 1 | const GoogleActionsService = require('../../../services/google-actions'); 2 | 3 | const gladys = {}; 4 | const serviceId = 'd1e45425-fe25-4968-ac0f-bc695d5202d9'; 5 | 6 | describe('GoogleActionsService', () => { 7 | const googleActionsService = GoogleActionsService(gladys, serviceId); 8 | it('should start service', async () => { 9 | await googleActionsService.start(); 10 | }); 11 | 12 | it('should stop service', async () => { 13 | await googleActionsService.stop(); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /server/test/services/mqtt/lib/status.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | 3 | const MqttHandler = require('../../../../services/mqtt/lib'); 4 | 5 | const serviceId = 'faea9c35-759a-44d5-bcc9-2af1de37b8b4'; 6 | 7 | describe('mqttHandler.status', () => { 8 | it('should get status', async () => { 9 | const mqttHandler = new MqttHandler({}, {}, serviceId); 10 | const status = mqttHandler.status(); 11 | 12 | expect(status).to.have.property('configured'); 13 | expect(status).to.have.property('connected'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /server/test/services/netatmo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-underscore-dangle": "off", 4 | "no-promise-executor-return": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /server/test/services/node-red/mockPassword.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | hash: (password) => 3 | new Promise((resolve, reject) => { 4 | resolve(password); 5 | }), 6 | compare: (password, hash) => 7 | new Promise((resolve, reject) => { 8 | resolve(true); 9 | }), 10 | generate: () => 'password', 11 | }; 12 | -------------------------------------------------------------------------------- /server/test/services/openweather/fakeOpenWeatherService.js: -------------------------------------------------------------------------------- 1 | const service = { 2 | weather: { 3 | get: () => 4 | Promise.resolve({ 5 | temperature: 54.87, 6 | humidity: 0.76, 7 | pressure: 1019.4, 8 | datetime: new Date('2019-03-28T07:50:18.000Z'), 9 | units: 'metric', 10 | wind_speed: 5.25, 11 | weather: 'cloud', 12 | }), 13 | }, 14 | }; 15 | 16 | module.exports = service; 17 | -------------------------------------------------------------------------------- /server/test/services/philips-hue/utils/parseExternalId.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { parseExternalId } = require('../../../../services/philips-hue/lib/utils/parseExternalId'); 3 | 4 | describe('parseExternalId', () => { 5 | it('should return lightId and bridge serial number', () => { 6 | const { bridgeSerialNumber, lightId } = parseExternalId('philips-hue-light:serial-number:1'); 7 | expect(bridgeSerialNumber).to.equal('serial-number'); 8 | expect(lightId).to.equal(1); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /server/test/services/tasmota/lib/mock/TasmotaProtocolHandlerMock.test.js: -------------------------------------------------------------------------------- 1 | const { fake } = require('sinon'); 2 | 3 | const TasmotaHTTPHandlerMock = function TasmotaHTTPHandlerMock() {}; 4 | 5 | TasmotaHTTPHandlerMock.prototype.connect = fake.returns(null); 6 | TasmotaHTTPHandlerMock.prototype.disconnect = fake.returns(null); 7 | TasmotaHTTPHandlerMock.prototype.scan = fake.returns(null); 8 | TasmotaHTTPHandlerMock.prototype.getValue = fake.returns(null); 9 | 10 | module.exports = TasmotaHTTPHandlerMock; 11 | -------------------------------------------------------------------------------- /server/test/services/tasmota/tasmota.mock.test.js: -------------------------------------------------------------------------------- 1 | const { fake } = require('sinon'); 2 | 3 | const TasmotaHandler = function TasmotaHandler(gladys, serviceId) { 4 | this.gladys = gladys; 5 | this.serviceId = serviceId; 6 | this.mqttService = null; 7 | this.mqttDevices = {}; 8 | }; 9 | 10 | TasmotaHandler.prototype.connect = fake.returns(null); 11 | TasmotaHandler.prototype.disconnect = fake.returns(null); 12 | TasmotaHandler.prototype.handleMessage = fake.returns(null); 13 | 14 | module.exports = TasmotaHandler; 15 | -------------------------------------------------------------------------------- /server/test/services/telegram/TelegramApiMock.test.js: -------------------------------------------------------------------------------- 1 | const { fake } = require('sinon'); 2 | const EventEmitter = require('events'); 3 | 4 | class TelegramApiMock extends EventEmitter { 5 | constructor(token) { 6 | super(); 7 | this.token = token; 8 | } 9 | } 10 | 11 | TelegramApiMock.prototype.getMe = fake.resolves({ 12 | username: 'faketelegrambot', 13 | }); 14 | 15 | TelegramApiMock.prototype.sendMessage = fake.resolves(null); 16 | TelegramApiMock.prototype.sendPhoto = fake.resolves(null); 17 | 18 | module.exports = TelegramApiMock; 19 | -------------------------------------------------------------------------------- /server/test/services/telegram/index.test.js: -------------------------------------------------------------------------------- 1 | const TelegramService = require('../../../services/telegram'); 2 | 3 | const gladys = { 4 | variable: { 5 | getValue: () => Promise.resolve('TELEGRAM_API_KEY'), 6 | }, 7 | }; 8 | 9 | describe('telegram', () => { 10 | const telegramService = TelegramService(gladys, 'f87b7af2-ca8e-44fc-b754-444354b42fee'); 11 | it('should start service', async () => { 12 | await telegramService.start(); 13 | }); 14 | it('should stop service', async () => { 15 | await telegramService.stop(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /server/test/services/tp-link/mocks.test.js: -------------------------------------------------------------------------------- 1 | const { stub } = require('sinon'); 2 | const { Client } = require('../../../services/tp-link/node_modules/tplink-smarthome-api'); 3 | 4 | const MockedTpLinkApiClient = stub(Client); 5 | 6 | module.exports = { Client: MockedTpLinkApiClient }; 7 | -------------------------------------------------------------------------------- /server/test/services/tp-link/utils/parseExternalId.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { parseExternalId } = require('../../../../services/tp-link/lib/utils/parseExternalId'); 3 | 4 | describe('parseExternalId', () => { 5 | it('should return deviceId', () => { 6 | const deviceId = parseExternalId('tp-link:A2E4B5'); 7 | expect(deviceId).to.equal('A2E4B5'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /server/test/services/tuya/tuya.mock.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | const client = { 4 | init: sinon.stub(), 5 | }; 6 | 7 | const TuyaContext = function TuyaContext() { 8 | this.client = client; 9 | }; 10 | 11 | module.exports = { 12 | TuyaContext, 13 | client, 14 | }; 15 | -------------------------------------------------------------------------------- /server/test/services/xiaomi/DgramMock.test.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | const { fake } = require('sinon'); 3 | 4 | const dgram = {}; 5 | 6 | const socket = new EventEmitter(); 7 | // @ts-ignore 8 | socket.bind = fake.returns(null); 9 | // @ts-ignore 10 | socket.addMembership = fake.returns(null); 11 | // @ts-ignore 12 | socket.send = fake.returns(null); 13 | 14 | dgram.createSocket = fake.returns(socket); 15 | 16 | module.exports = dgram; 17 | -------------------------------------------------------------------------------- /server/test/services/zigbee2mqtt/utils/payloads/index.js: -------------------------------------------------------------------------------- 1 | const CCT5015 = require('./CCT5015.json'); 2 | const ZSSZKTHL = require('./ZSS-ZK-THL.json'); 3 | 4 | module.exports = [CCT5015, ZSSZKTHL]; 5 | -------------------------------------------------------------------------------- /server/test/utils/jwtSecret.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { generateJwtSecret } = require('../../utils/jwtSecret'); 3 | 4 | describe('generateJwtSecret', () => { 5 | it('should generate a jwtSecret with the right length', async () => { 6 | const jwtSecret = generateJwtSecret(); 7 | expect(jwtSecret).to.have.lengthOf(500); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /server/utils/buildExpandObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Return an object of expanded fields. 3 | * @param {string} expand - The expand string. 4 | * @returns {object} The fields object. 5 | * @example 6 | * buildExpandObject('temperature,humidity'); 7 | */ 8 | function buildExpandObject(expand) { 9 | if (!expand) { 10 | return {}; 11 | } 12 | const fields = {}; 13 | expand.split(',').forEach((prop) => { 14 | fields[prop] = true; 15 | }); 16 | return fields; 17 | } 18 | 19 | module.exports = { 20 | buildExpandObject, 21 | }; 22 | -------------------------------------------------------------------------------- /server/utils/cache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We want to cache some data in RAM. 3 | */ 4 | class Cache { 5 | constructor() { 6 | this.clear(); 7 | } 8 | 9 | set(key, value) { 10 | this.store[key] = value; 11 | } 12 | 13 | get(key) { 14 | return this.store[key]; 15 | } 16 | 17 | del(key) { 18 | delete this.store[key]; 19 | } 20 | 21 | clear() { 22 | this.store = {}; 23 | } 24 | } 25 | 26 | module.exports = { 27 | Cache, 28 | }; 29 | -------------------------------------------------------------------------------- /server/utils/getConfig.js: -------------------------------------------------------------------------------- 1 | const config = require('../config/config'); 2 | 3 | module.exports = () => { 4 | const env = process.env.NODE_ENV || 'development'; 5 | return config[env]; 6 | }; 7 | -------------------------------------------------------------------------------- /server/utils/logger.js: -------------------------------------------------------------------------------- 1 | const tracer = require('tracer'); 2 | 3 | const LOG_LEVEL = process.env.NODE_ENV === 'production' ? 'info' : 'log'; 4 | 5 | const logger = tracer.colorConsole({ level: LOG_LEVEL }); 6 | 7 | module.exports = logger; 8 | -------------------------------------------------------------------------------- /server/utils/titleize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Converts first letter of each word to uppercase. (useful for name or title). 3 | * @param {string} str - The object/string to transform. 4 | * @returns {string} Return the titleized string. 5 | * @example 6 | * titleize("Living room LIGHT"); => "Living Room Light" 7 | */ 8 | function titleize(str) { 9 | const newStr = !str ? '' : str; 10 | 11 | return newStr.toLowerCase().replace(/(?:^|\s|-)\S/g, (c) => { 12 | return c.toUpperCase(); 13 | }); 14 | } 15 | 16 | module.exports = { 17 | titleize, 18 | }; 19 | --------------------------------------------------------------------------------