diff --git a/CODEOWNERS b/CODEOWNERS index 2569b21c93306..e129d743c8bf3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -218,6 +218,7 @@ /bundles/org.openhab.binding.vektiva/ @octa22 /bundles/org.openhab.binding.velbus/ @cedricboon /bundles/org.openhab.binding.velux/ @gs4711 +/bundles/org.openhab.binding.verisure/ @jannegpriv /bundles/org.openhab.binding.vigicrues/ @clinique /bundles/org.openhab.binding.vitotronic/ @steand /bundles/org.openhab.binding.volvooncall/ @clinique diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 82ffd78cf1fff..c210a5b03f7a5 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1089,6 +1089,11 @@ org.openhab.binding.velux ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.verisure + ${project.version} + org.openhab.addons.bundles org.openhab.binding.vigicrues diff --git a/bundles/org.openhab.binding.verisure/.classpath b/bundles/org.openhab.binding.verisure/.classpath new file mode 100644 index 0000000000000..615608997a6c5 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.verisure/.project b/bundles/org.openhab.binding.verisure/.project new file mode 100644 index 0000000000000..10bc8e87a0831 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.verisure + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.verisure/NOTICE b/bundles/org.openhab.binding.verisure/NOTICE new file mode 100644 index 0000000000000..0c13fa7419c02 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/NOTICE @@ -0,0 +1,20 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons + +== Third-party Content + +jsoup +* License: MIT License +* Project: https://jsoup.org/ +* Source: https://github.com/jhy/jsoup diff --git a/bundles/org.openhab.binding.verisure/README.md b/bundles/org.openhab.binding.verisure/README.md new file mode 100644 index 0000000000000..646264cd14236 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/README.md @@ -0,0 +1,585 @@ +# Verisure Binding + +This is an openHAB binding for Verisure Alarm System, by Securitas Direct. + +This binding uses the rest API behind the Verisure My Pages: + +https://mypages.verisure.com/login.html. + +Be aware that Verisure don't approve if you update to often, I have gotten no complaints running with a 10 minutes update interval, but officially you should use 30 minutes. + + +## Supported Things + +This binding supports the following thing types: + +- Bridge +- Alarm +- Smoke Detector (climate) +- Water Detector (climate) +- Siren (climate) +- Night Control +- Yaleman SmartLock +- SmartPlug +- Door/Window Status +- User Presence Status +- Broadband Connection Status +- Mice Detection Status (incl. climate) +- Event Log +- Gateway + + +## Binding Configuration + +You will have to configure the bridge with username and password, these must be the same credentials as used when logging into https://mypages.verisure.com. + +You must also configure your pin-code(s) to be able to lock/unlock the SmartLock(s) and arm/unarm the Alarm(s). + +**NOTE:** To be able to have full control over all SmartLock functionality, the user has to have Administrator rights. + +## Discovery + +After the configuration of the Verisure Bridge all of the available Sensors, Alarms, SmartPlugs, SmartLocks, Climate and Mice Detection devices will be discovered and placed as things in the inbox. + +## Thing Configuration + +Only the bridge require manual configuration. The devices and sensors can be added by hand, or you can let the discovery mechanism automatically find all of your Verisure things. + +## Enable Debugging + +To enable DEBUG logging for the binding, login to Karaf console and enter: + +`openhab> log:set DEBUG org.openhab.binding.verisure` + +## Supported Things and Channels + +### Verisure Bridge + +#### Configuration Options + +* `username` - The username used to connect to http://mypage.verisure.com + * The user has to have Administrator rights to have full SmartLock functionality + +* `password` - The password used to connect to http://mypage.verisure.com + +* `refresh` - Specifies the refresh interval in seconds + +* `pin` - The username's pin code to arm/disarm alarm and lock/unlock door. In the case of more than one installation and different pin-codes, use a comma separated string where pin-code matches order of installations. The installation order can be found using DEBUG log settings. + * Two installations where the first listed installation uses a 6 digit pin-code and second listed installation uses a 4 digit pin-code: 123456,1234 + +If you define the bridge in a things-file the bridge type id is defined as `bridge`, e.g.: + +`Bridge verisure:bridge:myverisureBridge verisure:bridge:myverisure` + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|-----------------|-----------|-------------------------------------------------------------------------------------------------| +| status | String | This channel can be used to trigger an instant refresh by sending a RefreshType.REFRESH command.| + + +### Verisure Alarm + +#### Configuration Options + +* `deviceId` - Device Id + * Since Alarm lacks a Verisure ID, the following naming convention is used for alarm on installation ID 123456789: 'alarm123456789'. Installation ID can be found using DEBUG log settings + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|---------------------|-----------|-------------------------------------------------------------------------------------------| +| changedByUser | String | This channel reports the user that last changed the state of the alarm. | +| changedVia | String | This channel reports the method used to change the status. | +| timestamp | DateTime | This channel reports the last time the alarm status was changed. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| alarmStatus | String | This channel is used to arm/disarm the alarm. Available alarm status are "DISARMED", "ARMED_HOME" and "ARMED_AWAY".| +| alarmTriggerChannel | trigger | This is a trigger channel that receives events.| + +### Verisure Yaleman SmartLock + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|-------------------------|-----------|----------------------------------------------------------------------------------------------------------| +| changedByUser | String | This channel reports the user that last changed the state of the alarm. | +| timestamp | DateTime | This channel reports the last time the alarm status was changed. | +| changedVia | String | This channel reports the method used to change the status. | +| motorJam | Switch | This channel reports if the SmartLock motor has jammed. | +| location | String | This channel reports the location of the device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| smartLockStatus | Switch | This channel is used to lock/unlock. | +| autoRelock | Switch | This channel is used to configure auto-lock functionality. Only supported for users with Administrator rights. | +| smartLockVolume | String | This channel is used to set the volume level. Available volume settings are "SILENCE", "LOW" and "HIGH". Only supported for users with Administrator rights.| +| smartLockVoiceLevel | String | This channel is used to set the voice level. Available voice level settings are "ESSENTIAL" and "NORMAL". Only supported for users with Administrator rights.| +| smartLockTriggerChannel | trigger | This is a trigger channel that receives events. | + +### Verisure SmartPlug + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the sensor itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|-------------------------|-----------|-------------------------------------------------------------------| +| hazardous | Switch | This channel reports if the smart plug is configured as hazardous.| +| location | String | This channel reports the location of the device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| smartPlugStatus | Switch | This channel is used to turn smart plug on/off. | +| smartPlugTriggerChannel | trigger | This is a trigger channel that receives events. | + +### Verisure Smoke Detector + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or on the sensor itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|-----------------------------|-----------------------|-----------------------------------------------------------------------------| +| temperature | Number:Temperature | This channel reports the current temperature. | +| humidity | Number | This channel reports the current humidity in percentage. | +| humidityEnabled | Switch | This channel reports if the Climate is device capable of reporting humidity.| +| timestamp | DateTime | This channel reports the last time this sensor was updated. | +| location | String | This channel reports the location of the device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| smokeDetectorTriggerChannel | trigger | This is a trigger channel that receives events.| + +### Verisure Water Detector + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the sensor itself) + +#### Channels + +The following channels are supported: + + +| Channel Type ID | Item Type | Description | +|-----------------------------|-----------------------|--------------------------------------------------------------| +| temperature | Number:Temperature | This channel reports the current temperature. | +| timestamp | DateTime | This channel reports the last time this sensor was updated. | +| location | String | This channel reports the location of the device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| waterDetectorTriggerChannel | trigger | This is a trigger channel that receives events. | + + +### Verisure Siren + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the sensor itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|---------------------|-----------------------|------------------------------------------------------------| +| temperature | Number:Temperature | This channel reports the current temperature. | +| timestamp | DateTime | This channel reports the last time this sensor was updated.| +| location | String | This channel reports the location. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| sirenTriggerChannel | trigger | This is a trigger channel that receives events. | + +### Verisure Night Control + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the sensor itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|----------------------------|-----------------------|------------------------------------------------------------| +| temperature | Number:Temperature | This channel reports the current temperature. | +| timestamp | DateTime | This channel reports the last time this sensor was updated.| +| location | String | This channel reports the location. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| nightControlTriggerChannel | trigger | This is a trigger channel that receives events. | + +### Verisure DoorWindow Sensor + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the sensor itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|--------------------------|-----------|-----------------------------------------------------------------------------| +| state | Contact | This channel reports the if the door/window is open or closed (OPEN/CLOSED).| +| timestamp | DateTime | This channel reports the last time this sensor was updated. | +| location | String | This channel reports the location of the device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| doorWindowTriggerChannel | trigger | This is a trigger channel that receives events. | + + +### Verisure User Presence + +#### Configuration Options + +* `deviceId` - Device Id + * Since User presence lacks a Verisure ID, it is constructed from the user's email address, where the '@' sign is removed, and the site id. The following naming convention is used for User presence on site id 123456789 for a user with email address test@gmail.com: 'uptestgmailcom123456789'. Installation ID can be found using DEBUG log settings. + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|--------------------|-----------|-------------------------------------------------------------------------| +| userLocationStatus | String | This channel reports the user presence status (HOME/AWAY). | +| timestamp | DateTime | This channel reports the last time the User Presence status was changed.| +| userName | String | This channel reports the user's name. | +| webAccount | String | This channel reports the user's email address. | +| userDeviceName | String | This channel reports the name of the user device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | + +### Verisure Broadband Connection + +#### Configuration Options + +* `deviceId` - Device Id + * Since Broadband connection lacks a Verisure ID, the following naming convention is used for Broadband connection on site id 123456789: 'bc123456789'. Installation ID can be found using DEBUG log settings. + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|-----------------|-----------|--------------------------------------------------------------------------------| +| connected | String | This channel reports the broadband connection status (true means connected). | +| timestamp | DateTime | This channel reports the last time the Broadband connection status was checked.| +| installationName| String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | + +### Verisure Mice Detection + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 5A4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the sensor itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|-----------------------------|--------------------|-------------------------------------------------------------------------------------| +| countLatestDetection | Number | This channel reports the number of mice counts the latest detection during last 24. | +| countLast24Hours | Number | This channel reports the total number of mice counts the last 24h. | +| durationLatestDetection | Number:Time | This channel reports the detection duration in min of latest detection. | +| durationLast24Hours | Number:Time | This channel reports the total detection duration in min for the last 24 hours. | +| timestamp | DateTime | This channel reports time for the last mouse detection. | +| temperature | Number:Temperature | This channel reports the current temperature. | +| temperatureTimestamp | DateTime | This channel reports the time for the last temperature reading. | +| location | String | This channel reports the location of the device. | +| installationName | String | This channel reports the installation name. | +| installationId | Number | This channel reports the installation ID. | +| miceDetectionTriggerChannel | trigger | This is a trigger channel that receives events. | + +### Verisure Event Log + +#### Configuration Options + +* `deviceId` - Device Id + * Since Event Log lacks a Verisure ID, the following naming convention is used for Event Log on site id 123456789: 'el123456789'. Installation ID can be found using DEBUG log settings. + + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|---------------------|-----------|-------------------------------------------------------------------------| +| lastEventLocation | String | This channel reports location for last event in event log. | +| lastEventDeviceId | String | This channel reports device ID for last event in event log. | +| lastEventDeviceType | String | This channel reports device type for last event in event log. | +| lastEventType | String | This channel reports type for last event in event log. | +| lastEventCategory | String | This channel reports category for last event in event log. | +| lastEventTime | DateTime | This channel reports time for last event in event log. | +| lastEventUserName | String | This channel reports user name for last event in event log. | +| eventLog | String | This channel reports the last 15 events from event log in a JSON array. | + +### Verisure Gateway + +#### Configuration Options + +* `deviceId` - Device Id + * Sensor Id. Example 3B4C35FT (Note: Verisure ID, found in the Verisure App or My Pages or on the Gateway itself) + +#### Channels + +The following channels are supported: + +| Channel Type ID | Item Type | Description | +|---------------------|-----------|----------------------------------------------------------------------| +| model | String | This channel reports gateway model. | +| location | String | This channel reports gateway location. | +| statusGSMOverUDP | String | This channel reports communication status for GSM over UDP. | +| testTimeGSMOverUDP | DateTime | This channel reports last communication test time for GSM over UDP. | +| statusGSMOverSMS | String | This channel reports communication status for GSM over SMS. | +| testTimeGSMOverSMS | DateTime | This channel reports last communication test time for GSM over SMS. | +| statusGPRSOverUDP | String | This channel reports communication status for GPRS over UDP. | +| testTimeGPRSOverUDP | DateTime | This channel reports last communication test time for GPRS over UDP. | +| statusETHOverUDP | String | This channel reports communication status for ETH over UDP. | +| testTimeETHOverUDP | DateTime | This channel reports last communication test time for ETH over UDP. | + +## Trigger Events + +To be able to get trigger events you need an active Event Log thing, you can either get it via auto-detection or create your own in a things-file. +The following trigger events are defined per thing type: + +| Event | Thing Type | Description | +|-------------------|---------------|------------------------------------------------------------| +| LOCK | SmartLock | SmartLock has been locked. | +| UNLOCK | SmartLock | SmartLock has been locked. | +| LOCK_FAILURE | SmartLock | SmartLock has failed to lock/unlock. | +| ARM | Alarm | Alarm has been armed. | +| DISARM | Alarm | Alarm has been disarmed. | +| DOORWINDOW_OPENED | DoorWindow | DoorWindow has detected a door/window that opened. | +| DOORWINDOW_CLOSED | DoorWindow | DoorWindow has detected a door/window that closed. | +| INTRUSION | DoorWindow | DoorWindow has detected an intrusion. | +| FIRE | SmokeDetector | SmokeDetector has detected fire/smoke. | +| WATER | WaterDetector | WaterDetector has detected a water leak. | +| MICE | MiceDetector | WaterMiceDetector has detected a mouse. | +| COM_FAILURE | All | Communication failure detected. | +| COM_RESTORED | All | Communication restored. | +| COM_TEST | All | Communication test. | +| BATTERY_LOW | All | Battery low level detected. | +| BATTERY_RESTORED | All | Battery level restored. | +| SABOTAGE_ALARM | All | Sabotage alarm detected. | +| SABOTAGE_RESTORED | All | Sabotage alarm restored. | + +## Example + +### Things-file + +```` +// Bridge configuration +Bridge verisure:bridge:myverisure "Verisure Bridge" [username="x@y.com", password="1234", refresh="600", pin="111111"] { + + Thing alarm JannesAlarm "Verisure Alarm" [ deviceId="alarm123456789" ] + Thing smartLock JannesSmartLock "Verisure Entrance Yale Doorman" [ deviceId="3C446NPO" ] + Thing smartPlug JannesSmartPlug "Verisure SmartPlug" [ deviceId="3D7GMANV" ] + Thing waterDetector JannesWaterDetector "Verisure Water Detector" [ deviceId="3WETQRH5" ] + Thing userPresence JannesUserPresence "Verisure User Presence" [ deviceId="uptestgmailcom123456789" ] + Thing eventLog JannesEventLog "Verisure Event Log" [ deviceId="el123456789" ] + Thing gateway JannesGateway "Verisure Gateway" [ deviceId="3AFG5673" ] +} +```` + +### Items-file + +```` +Group gVerisureMiceDetection +Group gVerisureEventLog +Group gVerisureGateway + +// SmartLock and Alarm +Switch SmartLock "Verisure SmartLock" [ "Switchable" ] {channel="verisure:smartLock:myverisure:JannesSmartLock:smartLockStatus"} +Switch AutoLock "AutoLock" [ "Switchable" ] {channel="verisure:smartLock:myverisure:JannesSmartLock:autoRelock"} +String SmartLockVolume "SmartLock Volume" {channel="verisure:smartLock:myverisure:JannesSmartLock:smartLockVolume"} +DateTime SmartLockLastUpdated "SmartLock Last Updated [%1$tY-%1$tm-%1$td %1$tR]" {channel="verisure:smartLock:myverisure:JannesSmartLock:timestamp"} +String AlarmHome "Alarm Home" {channel="verisure:alarm:myverisure:JannesAlarm:alarmStatus"} +DateTime AlarmLastUpdated "Verisure Alarm Last Updated [%1$tY-%1$tm.%1$td %1$tR]" {channel="verisure:alarm:myverisure:JannesAlarm:timestamp"} +String AlarmChangedByUser "Verisure Alarm Changed By User" {channel="verisure:alarm:myverisure:JannesAlarm:changedByUser"} + + +// SmartPlugs +Switch SmartPlugLamp "SmartPlug" [ "Switchable" ] {channel="verisure:smartPlug:myverisure:4ED5ZXYC:smartPlugStatus"} +Switch SmartPlugGlavaRouter "SmartPlug Glava Router" [ "Switchable" ] {channel="verisure:smartPlug:myverisure:JannesSmartPlug:smartPlugStatus"} + +// DoorWindow +String DoorWindowLocation "Door Window Location" {channel="verisure:doorWindowSensor:myverisure:1SG5GHGT:location"} +String DoorWindowStatus "Door Window Status" {channel="verisure:doorWindowSensor:myverisure:1SG5GHGT:state"} + +// UserPresence +String UserName "User Name" {channel="verisure:userPresence:myverisure:JannesUserPresence:userName"} +String UserLocationEmail "User Location Email" {channel="verisure:userPresence:myverisure:JannesUserPresence:webAccount"} +String UserLocationName "User Location Name" {channel="verisure:userPresence:myverisure:JannesUserPresence:userLocationStatus"} +String UserNameGlava "User Name Glava" {channel="verisure:userPresence:myverisure:userpresencetestgmailcom123456789:userName"} +String UserLocationEmailGlava "User Location Email Glava" {channel="verisure:userPresence:myverisure:userpresencetestgmailcom123456789:webAccount"} +String UserLocationNameGlava "User Location Name Glava" {channel="verisure:userPresence:myverisure:userpresencetestgmailcom1123456789:userLocationStatus"} + +// EventLog +String LastEventLocation "Last Event Location" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventLocation"} +String LastEventDeviceId "Last Event Device ID" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventDeviceId"} +String LastEventDeviceType "Last Event Device Type" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventDeviceType"} +String LastEventType "Last Event Type" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventType"} +String LastEventCategory "Last Event Category" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventCategory"} +DateTime LastEventTime "Last Event Time [%1$tY-%1$tm-%1$td %1$tR]" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventTime"} +String LastEventUserName "Last Event User Name" (gVerisureEventLog) {channel="verisure:eventLog:myverisure:JannesEventLog:lastEventUserName"} +String EventLog "Event Log" {channel="verisure:eventLog:myverisure:JannesEventLog:eventLog"} + +// Gateway +String VerisureGatewayModel "Gateway Model" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:model"} +String VerisureGatewayLocation "Gateway Location" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:location"} +String VerisureGWStatusGSMOverUDP "Gateway Status GSMOverUDP" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:statusGSMOverUDP"} +DateTime VerisureGWTestTimeGSMOverUDP "Gateway Test Time GSMOverUDP" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:testTimeGSMOverUDP"} +String VerisureGWStatusGSMOverSMS "Gateway Status GSMOverSMS" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:statusGSMOverSMS"} +DateTime VerisureGWTestTimeGSMOverSMS "Gateway Test Time GSMOverSMS" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:testTimeGSMOverSMS"} +String VerisureGWStatusGPRSOverUDP "Gateway Status GPRSOverUDP" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:statusGPRSOverUDP"} +DateTime VerisureGWTestTimeGPRSOverUDP "Gateway Test Time GPRSOverUDP" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:testTimeGPRSOverUDP"} +String VerisureGWStatusETHOverUDP "Gateway Status ETHOverUDP" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:statusETHOverUDP"} +DateTime VerisureGWTestTimeETHOverUDP "Gateway Test Time ETHOverUDP" (gVerisureGateway) {channel="verisure:gateway:myverisure:JannesGateway:testTimeETHOverUDP"} + +// Broadband Connection +String CurrentBBStatus "Broadband Connection Status" {channel="verisure:broadbandConnection:myverisure:bc123456789:connected"} + +// Verisure Mice Detection +Number MouseCountLastDetection "Mouse Count Last Detection" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:countLatestDetection"} +Number MouseCountLast24Hours "Mouse Count Last 24 Hours" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:countLast24Hours"} +DateTime MouseLastDetectionTime "Mouse Last Detection Time [%1$tY-%1$tm-%1$td %1$tR]" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:timestamp"} +Number MouseDurationLastDetection "Mouse Duration Last Detection" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:durationLatestDetection"} +Number MouseDurationLast24Hours "Mouse Duration Last 24 Hours" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:durationLast24Hours"} +Number MouseDetectionTemperature "Mouse Detection Temperature [%.1f C]" (gTemperaturesVerisure, gVerisureMiceDetection) ["CurrentTemperature"] {channel="verisure:miceDetection:myverisure:2CFZH80U:temperature"} +DateTime MouseDetectionTemperatureTime "Mouse Detection Temperature Time [%1$tY-%1$tm-%1$td %1$tR]" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:temperatureTimestamp"} +String MouseDetectionLocation "Mouse Detection Location" (gVerisureMiceDetection) {channel="verisure:miceDetection:myverisure:2CFZH80U:location"} + +```` + +### Sitemap + +```` + Frame label="SmartLock and Alarm" { + Text label="SmartLock and Alarm" icon="groundfloor" { + Frame label="Yale Doorman SmartLock" { + Switch item=SmartLock label="Yale Doorman SmartLock" icon="lock.png" + } + Frame label="Verisure Alarm" { + Switch item=AlarmHome icon="alarm" label="Verisure Alarm" mappings=["DISARMED"="Disarm", "ARMED_HOME"="Arm Home", "ARMED_AWAY"="Arm Away"] + } + Frame label="Yale Doorman SmartLock AutoLock" { + Switch item=AutoLock label="Yale Doorman SmartLock AutoLock" icon="lock.png" + } + Frame label="Yale Doorman SmartLock Volume" { + Switch item=SmartLockVolume icon="lock" label="Yale Doorman SmartLock Volume" mappings=["SILENCE"="Silence", "LOW"="Low", "HIGH"="High"] + } + Text item=AlarmHomeInstallationName label="Alarm Installation [%s]" + Text item=AlarmChangedByUser label="Changed by user [%s]" + Text item=AlarmLastUpdated + Text item=SmartLockStatus label="SmartLock status [%s]" + Text item=SmartLockLastUpdated + Text item=SmartLockOperatedBy label="Changed by user [%s]" + Text item=DoorWindowStatus label="Door State" + Text item=DoorWindowLocation + } + } + + Frame label="SmartPlugs" { + Text label="SmartPlugs" icon="attic" { + Frame label="SmartPlug Lamp" { + Switch item=SmartPlugLamp label="Verisure SmartPlug Lamp" icon="smartheater.png" + } + } + } + + Frame label="User Presence" { + Text label="User Presence" icon="attic" { + Frame label="User Presence Champinjonvägen" { + Text item=UserName label="User Name [%s]" + Text item=UserLocationEmail label="User Email [%s]" + Text item=UserLocationStatus label="User Location Status [%s]" + } + } + } + + Frame label="Broadband Connection" { + Text label="Broadband Connection" icon="attic" { + Frame label="Broadband Connection Champinjonvägen" { + Text item=CurrentBBStatus label="Broadband Connection Status [%s]" + } + } + } + + Frame label="Mice Detection" { + Group item=gVerisureMiceDetection label="Verisure Mice Detection" + } + + Frame label="Event Log" { + Group item=gVerisureEventLog label="Verisure Event Log" + } + + Frame label="Gateway" { + Group item=gVerisureGateway label="Verisure Gateway" + } + +```` + +### Rules + +```` +import org.eclipse.smarthome.core.types.RefreshType + +rule "Handle Refesh of Verisure" +when + Item RefreshVerisure received command +then + var String command = RefreshVerisure.state.toString.toLowerCase + logDebug("RULES","RefreshVerisure Rule command: " + command) + sendCommand(VerisureBridgeStatus, RefreshType.REFRESH) +end + +rule "Verisure SmartLock Event Triggers" +when + Channel "verisure:smartLock:myverisure:JannesSmartLock:smartLockTriggerChannel" triggered +then + logInfo("RULES", "A SmartLock trigger event was detected:" + receivedEvent.toString()) +end + +rule "Verisure Gateway Event Triggers" +when + Channel "verisure:gateway:myverisure:JannesGateway:gatewayTriggerChannel" triggered +then + logInfo("RULES", "A Gateway trigger event was detected:" + receivedEvent.toString()) +end + +rule "Verisure DoorWindow Event Triggers" +when + Channel "verisure:doorWindowSensor:myverisure:1SG5GHGT:doorWindowTriggerChannel" triggered +then + logInfo("RULES", "A DoorWindow trigger event was detected:" + receivedEvent.toString()) +end + + +```` + diff --git a/bundles/org.openhab.binding.verisure/pom.xml b/bundles/org.openhab.binding.verisure/pom.xml new file mode 100644 index 0000000000000..f20a67e2624b9 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.7-SNAPSHOT + + + org.openhab.binding.verisure + + openHAB Add-ons :: Bundles :: Verisure Binding + + + + org.jsoup + jsoup + 1.8.3 + compile + + + + diff --git a/bundles/org.openhab.binding.verisure/src/main/feature/feature.xml b/bundles/org.openhab.binding.verisure/src/main/feature/feature.xml new file mode 100644 index 0000000000000..64a788f07f1d5 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/feature/feature.xml @@ -0,0 +1,10 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + openhab-runtime-base + openhab-transport-serial + mvn:org.jsoup/jsoup/1.8.3 + mvn:org.openhab.addons.bundles/org.openhab.binding.verisure/${project.version} + + diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/DeviceStatusListener.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/DeviceStatusListener.java new file mode 100644 index 0000000000000..fe8e55a718f17 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/DeviceStatusListener.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.verisure.internal.dto.VerisureThingDTO; + +/** + * The {@link DeviceStatusListener} is notified when a device status has changed + * or a device has been removed or added. + * + * @author Jarle Hjortland - Initial contribution + * @author Jan Gustafsson - Updated after code review comments + * + */ +@NonNullByDefault +public interface DeviceStatusListener { + + /** + * This method is called whenever the state of the given device has changed. + * + * @param thing + * The thing that was changed. + */ + void onDeviceStateChanged(T thing); + + /** + * This method returns the thing's class + */ + public Class getVerisureThingClass(); +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java new file mode 100644 index 0000000000000..d4b10d5c9157f --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBindingConstants.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link VerisureBinding} class defines common constants, which are + * used across the whole binding. + * + * @author l3rum - Initial contribution + * @author Jan Gustafsson - Furher development + */ +@NonNullByDefault +public class VerisureBindingConstants { + + public static final String BINDING_ID = "verisure"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); + public static final ThingTypeUID THING_TYPE_ALARM = new ThingTypeUID(BINDING_ID, "alarm"); + public static final ThingTypeUID THING_TYPE_SMARTPLUG = new ThingTypeUID(BINDING_ID, "smartPlug"); + public static final ThingTypeUID THING_TYPE_SMOKEDETECTOR = new ThingTypeUID(BINDING_ID, "smokeDetector"); + public static final ThingTypeUID THING_TYPE_WATERDETECTOR = new ThingTypeUID(BINDING_ID, "waterDetector"); + public static final ThingTypeUID THING_TYPE_SIREN = new ThingTypeUID(BINDING_ID, "siren"); + public static final ThingTypeUID THING_TYPE_DOORWINDOW = new ThingTypeUID(BINDING_ID, "doorWindowSensor"); + public static final ThingTypeUID THING_TYPE_USERPRESENCE = new ThingTypeUID(BINDING_ID, "userPresence"); + public static final ThingTypeUID THING_TYPE_SMARTLOCK = new ThingTypeUID(BINDING_ID, "smartLock"); + public static final ThingTypeUID THING_TYPE_BROADBAND_CONNECTION = new ThingTypeUID(BINDING_ID, + "broadbandConnection"); + public static final ThingTypeUID THING_TYPE_NIGHT_CONTROL = new ThingTypeUID(BINDING_ID, "nightControl"); + public static final ThingTypeUID THING_TYPE_MICE_DETECTION = new ThingTypeUID(BINDING_ID, "miceDetection"); + public static final ThingTypeUID THING_TYPE_EVENT_LOG = new ThingTypeUID(BINDING_ID, "eventLog"); + public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway"); + + // List of all Channel ids + public static final String CHANNEL_NUMERIC_STATUS = "numericStatus"; + public static final String CHANNEL_TEMPERATURE = "temperature"; + public static final String CHANNEL_HUMIDITY = "humidity"; + public static final String CHANNEL_HUMIDITY_ENABLED = "humidityEnabled"; + public static final String CHANNEL_LOCATION = "location"; + public static final String CHANNEL_STATUS = "status"; + public static final String CHANNEL_CONNECTED = "connected"; + public static final String CHANNEL_STATE = "state"; + public static final String CHANNEL_LABEL = "label"; + public static final String CHANNEL_USER_NAME = "userName"; + public static final String CHANNEL_WEBACCOUNT = "webAccount"; + public static final String CHANNEL_USER_LOCATION_STATUS = "userLocationStatus"; + public static final String CHANNEL_USER_DEVICE_NAME = "userDeviceName"; + public static final String CHANNEL_SMARTLOCK_VOLUME = "smartLockVolume"; + public static final String CHANNEL_SMARTLOCK_VOICE_LEVEL = "smartLockVoiceLevel"; + public static final String CHANNEL_SMARTLOCK_TRIGGER_CHANNEL = "smartLockTriggerChannel"; + public static final String CHANNEL_AUTO_RELOCK = "autoRelock"; + public static final String CHANNEL_SMARTPLUG_STATUS = "smartPlugStatus"; + public static final String CHANNEL_SMARTPLUG_TRIGGER_CHANNEL = "smartPlugTriggerChannel"; + public static final String CHANNEL_ALARM_STATUS = "alarmStatus"; + public static final String CHANNEL_ALARM_TRIGGER_CHANNEL = "alarmTriggerChannel"; + public static final String CHANNEL_SMARTLOCK_STATUS = "smartLockStatus"; + public static final String CHANNEL_CHANGED_BY_USER = "changedByUser"; + public static final String CHANNEL_CHANGED_VIA = "changedVia"; + public static final String CHANNEL_TIMESTAMP = "timestamp"; + public static final String CHANNEL_TEMPERATURE_TIMESTAMP = "temperatureTimestamp"; + public static final String CHANNEL_HAZARDOUS = "hazardous"; + public static final String CHANNEL_MOTOR_JAM = "motorJam"; + public static final String CHANNEL_INSTALLATION_NAME = "installationName"; + public static final String CHANNEL_INSTALLATION_ID = "installationId"; + public static final String CHANNEL_COUNT_LATEST_DETECTION = "countLatestDetection"; + public static final String CHANNEL_COUNT_LAST_24_HOURS = "countLast24Hours"; + public static final String CHANNEL_DURATION_LATEST_DETECTION = "durationLatestDetection"; + public static final String CHANNEL_DURATION_LAST_24_HOURS = "durationLast24Hours"; + public static final String CHANNEL_LAST_EVENT_LOCATION = "lastEventLocation"; + public static final String CHANNEL_LAST_EVENT_ID = "lastEventId"; + public static final String CHANNEL_LAST_EVENT_DEVICE_ID = "lastEventDeviceId"; + public static final String CHANNEL_LAST_EVENT_DEVICE_TYPE = "lastEventDeviceType"; + public static final String CHANNEL_LAST_EVENT_TYPE = "lastEventType"; + public static final String CHANNEL_LAST_EVENT_CATEGORY = "lastEventCategory"; + public static final String CHANNEL_LAST_EVENT_TIME = "lastEventTime"; + public static final String CHANNEL_LAST_EVENT_USER_NAME = "lastEventUserName"; + public static final String CHANNEL_EVENT_LOG = "eventLog"; + public static final String CHANNEL_STATUS_GSM_OVER_UDP = "statusGSMOverUDP"; + public static final String CHANNEL_STATUS_GSM_OVER_SMS = "statusGSMOverSMS"; + public static final String CHANNEL_STATUS_GPRS_OVER_UDP = "statusGPRSOverUDP"; + public static final String CHANNEL_STATUS_ETH_OVER_UDP = "statusETHOverUDP"; + public static final String CHANNEL_TEST_TIME_GSM_OVER_UDP = "testTimeGSMOverUDP"; + public static final String CHANNEL_TEST_TIME_GSM_OVER_SMS = "testTimeGSMOverSMS"; + public static final String CHANNEL_TEST_TIME_GPRS_OVER_UDP = "testTimeGPRSOverUDP"; + public static final String CHANNEL_TEST_TIME_ETH_OVER_UDP = "testTimeETHOverUDP"; + public static final String CHANNEL_GATEWAY_MODEL = "model"; + public static final String CHANNEL_SMOKE_DETECTION_TRIGGER_CHANNEL = "smokeDetectionTriggerChannel"; + public static final String CHANNEL_MICE_DETECTION_TRIGGER_CHANNEL = "miceDetectionTriggerChannel"; + public static final String CHANNEL_WATER_DETECTION_TRIGGER_CHANNEL = "waterDetectionTriggerChannel"; + public static final String CHANNEL_SIREN_TRIGGER_CHANNEL = "sirenTriggerChannel"; + public static final String CHANNEL_NIGHT_CONTROL_TRIGGER_CHANNEL = "nightControlTriggerChannel"; + public static final String CHANNEL_DOOR_WINDOW_TRIGGER_CHANNEL = "doorWindowTriggerChannel"; + public static final String CHANNEL_GATEWAY_TRIGGER_CHANNEL = "gatewayTriggerChannel"; + + // Trigger channel events + public static final String TRIGGER_EVENT_LOCK = "LOCK"; + public static final String TRIGGER_EVENT_UNLOCK = "UNLOCK"; + public static final String TRIGGER_EVENT_LOCK_FAILURE = "LOCK_FAILURE"; + public static final String TRIGGER_EVENT_ARM = "ARM"; + public static final String TRIGGER_EVENT_DISARM = "DISARM"; + public static final String TRIGGER_EVENT_FIRE = "FIRE"; + public static final String TRIGGER_EVENT_INSTRUSION = "INTRUSION"; + public static final String TRIGGER_EVENT_WATER = "WATER"; + public static final String TRIGGER_EVENT_MICE = "MICE"; + public static final String TRIGGER_EVENT_BATTERY_LOW = "BATTERY_LOW"; + public static final String TRIGGER_EVENT_BATTERY_RESTORED = "BATTERY_RESTORED"; + public static final String TRIGGER_EVENT_COM_FAILURE = "COM_FAILURE"; + public static final String TRIGGER_EVENT_COM_RESTORED = "COM_RESTORED"; + public static final String TRIGGER_EVENT_COM_TEST = "COM_TEST"; + public static final String TRIGGER_EVENT_SABOTAGE_ALARM = "SABOTAGE_ALARM"; + public static final String TRIGGER_EVENT_SABOTAGE_RESTORED = "SABOTAGE_RESTORED"; + public static final String TRIGGER_EVENT_DOORWINDOW_OPENED = "DOORWINDOW_OPENED"; + public static final String TRIGGER_EVENT_DOORWINDOW_CLOSED = "DOORWINDOW_CLOSED"; + public static final String TRIGGER_EVENT_LOCATION_HOME = "LOCATION_HOME"; + public static final String TRIGGER_EVENT_LOCATION_AWAY = "LOCATION_AWAY"; + + // REST URI constants + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + public static final String BASEURL = "https://mypages.verisure.com"; + public static final String LOGON_SUF = BASEURL + "/j_spring_security_check?locale=en_GB"; + public static final String ALARM_COMMAND = BASEURL + "/remotecontrol/armstatechange.cmd"; + public static final String SMARTLOCK_LOCK_COMMAND = BASEURL + "/remotecontrol/lockunlock.cmd"; + public static final String SMARTLOCK_SET_COMMAND = BASEURL + "/overview/setdoorlock.cmd"; + public static final String SMARTLOCK_AUTORELOCK_COMMAND = BASEURL + "/settings/setautorelock.cmd"; + public static final String SMARTLOCK_VOLUME_COMMAND = BASEURL + "/settings/setvolume.cmd"; + + public static final String SMARTPLUG_COMMAND = BASEURL + "/settings/smartplug/onoffplug.cmd"; + public static final String START_REDIRECT = "/uk/start.html"; + public static final String START_SUF = BASEURL + START_REDIRECT; + + // GraphQL constants + public static final String STATUS = BASEURL + "/uk/status"; + public static final String SETTINGS = BASEURL + "/uk/settings.html?giid="; + public static final String SET_INSTALLATION = BASEURL + "/setinstallation?giid="; + public static final String BASEURL_API = "https://m-api02.verisure.com"; + public static final String START_GRAPHQL = "/graphql"; + public static final String AUTH_TOKEN = "/auth/token"; + public static final String AUTH_LOGIN = "/auth/login"; + + public static final String ALARMSTATUS_PATH = "/remotecontrol"; + public static final String SMARTLOCK_PATH = "/overview/doorlock/"; + public static final String DOORWINDOW_PATH = "/settings/doorwindow"; + public static final String USERTRACKING_PATH = "/overview/usertrackingcontacts"; + public static final String CLIMATEDEVICE_PATH = "/overview/climatedevice"; + public static final String SMARTPLUG_PATH = "/settings/smartplug"; + public static final String ETHERNETSTATUS_PATH = "/overview/ethernetstatus"; + public static final String VACATIONMODE_PATH = "/overview/vacationmode"; + public static final String TEMPERATURE_CONTROL_PATH = "/overview/temperaturecontrol"; + public static final String MOUSEDETECTION_PATH = "/overview/mousedetection"; + public static final String CAMERA_PATH = "/overview/camera"; +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java new file mode 100644 index 0000000000000..0c3f01481eaa5 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureBridgeConfiguration.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Configuration class for VerisureBridgeHandler bridge used to connect to the + * Verisure MyPage. + * + * @author Jarle Hjortland - Initial contribution + */ +@NonNullByDefault +public class VerisureBridgeConfiguration { + public @Nullable String username; + public @Nullable String password; + public int refresh; + public @Nullable String pin; +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java new file mode 100644 index 0000000000000..0768b577fd5aa --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureHandlerFactory.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.eclipse.smarthome.io.net.http.HttpClientFactory; +import org.openhab.binding.verisure.internal.handler.VerisureAlarmThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureBridgeHandler; +import org.openhab.binding.verisure.internal.handler.VerisureBroadbandConnectionThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureClimateDeviceThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureDoorWindowThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureEventLogThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureGatewayThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureMiceDetectionThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureSmartLockThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureSmartPlugThingHandler; +import org.openhab.binding.verisure.internal.handler.VerisureUserPresenceThingHandler; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VerisureHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Jarle Hjortland - Initial contribution + * @author Jan Gustafsson - Further development + */ +@NonNullByDefault +@Component(service = ThingHandlerFactory.class, configurationPid = "binding.verisure") +public class VerisureHandlerFactory extends BaseThingHandlerFactory { + + public static final Set SUPPORTED_THING_TYPES = new HashSet(); + static { + SUPPORTED_THING_TYPES.addAll(VerisureBridgeHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureAlarmThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureSmartLockThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureSmartPlugThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureClimateDeviceThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureBroadbandConnectionThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureDoorWindowThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureUserPresenceThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureMiceDetectionThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureEventLogThingHandler.SUPPORTED_THING_TYPES); + SUPPORTED_THING_TYPES.addAll(VerisureGatewayThingHandler.SUPPORTED_THING_TYPES); + } + + private final Logger logger = LoggerFactory.getLogger(VerisureHandlerFactory.class); + private final HttpClient httpClient; + + @Activate + public VerisureHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + logger.debug("createHandler this: {}", thing); + final ThingHandler thingHandler; + if (VerisureBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureBridgeHandler"); + thingHandler = new VerisureBridgeHandler((Bridge) thing, httpClient); + } else if (VerisureAlarmThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureAlarmThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureAlarmThingHandler(thing); + } else if (VerisureSmartLockThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureSmartLockThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureSmartLockThingHandler(thing); + } else if (VerisureSmartPlugThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureSmartPlugThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureSmartPlugThingHandler(thing); + } else if (VerisureClimateDeviceThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureClimateDeviceThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureClimateDeviceThingHandler(thing); + } else if (VerisureBroadbandConnectionThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureBroadbandConnectionThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureBroadbandConnectionThingHandler(thing); + } else if (VerisureDoorWindowThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureDoorWindowThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureDoorWindowThingHandler(thing); + } else if (VerisureUserPresenceThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureUserPresenceThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureUserPresenceThingHandler(thing); + } else if (VerisureMiceDetectionThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureMiceDetectionThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureMiceDetectionThingHandler(thing); + } else if (VerisureEventLogThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureEventLogThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureEventLogThingHandler(thing); + } else if (VerisureGatewayThingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { + logger.debug("Create VerisureGatewayThingHandler {}", thing.getThingTypeUID()); + thingHandler = new VerisureGatewayThingHandler(thing); + } else { + logger.debug("Not possible to create thing handler for thing {}", thing); + thingHandler = null; + } + return thingHandler; + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java new file mode 100644 index 0000000000000..179425df21ced --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureSession.java @@ -0,0 +1,1012 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.math.BigDecimal; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpResponseException; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.BytesContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.openhab.binding.verisure.internal.dto.VerisureAlarmsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureBroadbandConnectionsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureClimatesDTO; +import org.openhab.binding.verisure.internal.dto.VerisureDoorWindowsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureEventLogDTO; +import org.openhab.binding.verisure.internal.dto.VerisureGatewayDTO; +import org.openhab.binding.verisure.internal.dto.VerisureGatewayDTO.CommunicationState; +import org.openhab.binding.verisure.internal.dto.VerisureInstallationsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureInstallationsDTO.Owainstallation; +import org.openhab.binding.verisure.internal.dto.VerisureMiceDetectionDTO; +import org.openhab.binding.verisure.internal.dto.VerisureSmartLockDTO; +import org.openhab.binding.verisure.internal.dto.VerisureSmartLocksDTO; +import org.openhab.binding.verisure.internal.dto.VerisureSmartPlugsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureThingDTO; +import org.openhab.binding.verisure.internal.dto.VerisureUserPresencesDTO; +import org.openhab.binding.verisure.internal.handler.VerisureThingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * This class performs the communication with Verisure My Pages. + * + * @author Jarle Hjortland - Initial contribution + * @author Jan Gustafsson - Re-design and support for several sites and update to new Verisure API + * + */ +@NonNullByDefault +public class VerisureSession { + + @NonNullByDefault({}) + private final Map verisureThings = new ConcurrentHashMap<>(); + private final Map> verisureHandlers = new ConcurrentHashMap<>(); + private final Logger logger = LoggerFactory.getLogger(VerisureSession.class); + private final Gson gson = new Gson(); + private final List> deviceStatusListeners = new CopyOnWriteArrayList<>(); + private final Map verisureInstallations = new ConcurrentHashMap<>(); + private static final List APISERVERLIST = Arrays.asList("https://m-api01.verisure.com", + "https://m-api02.verisure.com"); + private int apiServerInUseIndex = 0; + private int numberOfEvents = 15; + private static final String USER_NAME = "username"; + private static final String PASSWORD_NAME = "vid"; + private String apiServerInUse = APISERVERLIST.get(apiServerInUseIndex); + private String authstring = ""; + private @Nullable String csrf; + private @Nullable String pinCode; + private HttpClient httpClient; + private @Nullable String userName = ""; + private @Nullable String password = ""; + + public VerisureSession(HttpClient httpClient) { + this.httpClient = httpClient; + } + + public boolean initialize(@Nullable String authstring, @Nullable String pinCode, @Nullable String userName) { + if (authstring != null) { + this.authstring = authstring.substring(0); + this.pinCode = pinCode; + this.userName = userName; + // Try to login to Verisure + if (logIn()) { + return getInstallations(); + } else { + return false; + } + } + return false; + } + + public boolean refresh() { + try { + if (logIn()) { + updateStatus(); + return true; + } else { + return false; + } + } catch (HttpResponseException e) { + logger.warn("Failed to do a refresh {}", e.getMessage()); + return false; + } + } + + public int sendCommand(String url, String data, BigDecimal installationId) { + logger.debug("Sending command with URL {} and data {}", url, data); + try { + configureInstallationInstance(installationId); + int httpResultCode = setSessionCookieAuthLogin(); + if (httpResultCode == HttpStatus.OK_200) { + return postVerisureAPI(url, data); + } else { + return httpResultCode; + } + } catch (ExecutionException | InterruptedException | TimeoutException e) { + logger.debug("Failed to send command {}", e.getMessage()); + } + return HttpStatus.BAD_REQUEST_400; + } + + public boolean unregisterDeviceStatusListener( + DeviceStatusListener deviceStatusListener) { + return deviceStatusListeners.remove(deviceStatusListener); + } + + @SuppressWarnings("unchecked") + public boolean registerDeviceStatusListener(DeviceStatusListener deviceStatusListener) { + return deviceStatusListeners.add((DeviceStatusListener) deviceStatusListener); + } + + @SuppressWarnings({ "unchecked" }) + public @Nullable T getVerisureThing(String deviceId, Class thingType) { + VerisureThingDTO thing = verisureThings.get(deviceId); + if (thingType.isInstance(thing)) { + return (T) thing; + } + return null; + } + + public @Nullable T getVerisureThing(String deviceId) { + VerisureThingDTO thing = verisureThings.get(deviceId); + if (thing != null) { + @SuppressWarnings("unchecked") + T thing2 = (T) thing; + return thing2; + } + return null; + } + + public @Nullable VerisureThingHandler getVerisureThinghandler(String deviceId) { + VerisureThingHandler thingHandler = verisureHandlers.get(deviceId); + return thingHandler; + } + + public void setVerisureThingHandler(VerisureThingHandler vth, String deviceId) { + verisureHandlers.put(deviceId, vth); + }; + + public void removeVerisureThingHandler(String deviceId) { + verisureHandlers.remove(deviceId); + } + + public Collection getVerisureThings() { + return verisureThings.values(); + } + + public @Nullable String getCsrf() { + return csrf; + } + + public @Nullable String getPinCode() { + return pinCode; + } + + public String getApiServerInUse() { + return apiServerInUse; + } + + public void setApiServerInUse(String apiServerInUse) { + this.apiServerInUse = apiServerInUse; + } + + public String getNextApiServer() { + apiServerInUseIndex++; + if (apiServerInUseIndex > (APISERVERLIST.size() - 1)) { + apiServerInUseIndex = 0; + } + return APISERVERLIST.get(apiServerInUseIndex); + } + + public void setNumberOfEvents(int numberOfEvents) { + this.numberOfEvents = numberOfEvents; + } + + public void configureInstallationInstance(BigDecimal installationId) + throws ExecutionException, InterruptedException, TimeoutException { + csrf = getCsrfToken(installationId); + logger.debug("Got CSRF: {}", csrf); + // Set installation + String url = SET_INSTALLATION + installationId; + httpClient.GET(url); + } + + public @Nullable String getCsrfToken(BigDecimal installationId) + throws ExecutionException, InterruptedException, TimeoutException { + String html = null; + String url = SETTINGS + installationId; + + ContentResponse resp = httpClient.GET(url); + html = resp.getContentAsString(); + logger.trace("url: {} html: {}", url, html); + + Document htmlDocument = Jsoup.parse(html); + Element nameInput = htmlDocument.select("input[name=_csrf]").first(); + if (nameInput != null) { + return nameInput.attr("value"); + } else { + return null; + } + } + + public @Nullable String getPinCode(BigDecimal installationId) { + return verisureInstallations.get(installationId).getPinCode(); + } + + private void setPasswordFromCookie() { + CookieStore c = httpClient.getCookieStore(); + List cookies = c.getCookies(); + cookies.forEach(cookie -> { + logger.trace("Response Cookie: {}", cookie); + if (cookie.getName().equals(PASSWORD_NAME)) { + password = cookie.getValue(); + logger.debug("Fetching vid {} from cookie", password); + } + }); + } + + private void logTraceWithPattern(int responseStatus, String content) { + if (logger.isTraceEnabled()) { + String pattern = "(?m)^\\s*\\r?\\n|\\r?\\n\\s*(?!.*\\r?\\n)"; + String replacement = ""; + logger.trace("HTTP Response ({}) Body:{}", responseStatus, content.replaceAll(pattern, replacement)); + } + } + + private boolean areWeLoggedIn() throws ExecutionException, InterruptedException, TimeoutException { + logger.debug("Checking if we are logged in"); + String url = STATUS; + + ContentResponse response = httpClient.newRequest(url).method(HttpMethod.GET).send(); + String content = response.getContentAsString(); + logTraceWithPattern(response.getStatus(), content); + + switch (response.getStatus()) { + case HttpStatus.OK_200: + if (content.contains("MyPages")) { + setPasswordFromCookie(); + return true; + } else { + logger.debug("Not on mypages,verisure.com, we need to login again!"); + return false; + } + case HttpStatus.MOVED_TEMPORARILY_302: + // Redirection + logger.debug("Status code 302. Redirected. Probably not logged in"); + return false; + case HttpStatus.INTERNAL_SERVER_ERROR_500: + case HttpStatus.SERVICE_UNAVAILABLE_503: + throw new HttpResponseException( + "Status code " + response.getStatus() + ". Verisure service temporarily down", response); + default: + logger.debug("Status code {} body {}", response.getStatus(), content); + break; + } + return false; + } + + private @Nullable T getJSONVerisureAPI(String url, Class jsonClass) + throws ExecutionException, InterruptedException, TimeoutException, JsonSyntaxException { + logger.debug("HTTP GET: {}", BASEURL + url); + + ContentResponse response = httpClient.GET(BASEURL + url + "?_=" + System.currentTimeMillis()); + String content = response.getContentAsString(); + logTraceWithPattern(response.getStatus(), content); + + return gson.fromJson(content, jsonClass); + } + + private ContentResponse postVerisureAPI(String url, String data, boolean isJSON) + throws ExecutionException, InterruptedException, TimeoutException { + logger.debug("postVerisureAPI URL: {} Data:{}", url, data); + Request request = httpClient.newRequest(url).method(HttpMethod.POST); + if (isJSON) { + request.header("content-type", "application/json"); + } else { + if (csrf != null) { + request.header("X-CSRF-TOKEN", csrf); + } + } + request.header("Accept", "application/json"); + if (!data.equals("empty")) { + request.content(new BytesContentProvider(data.getBytes(StandardCharsets.UTF_8)), + "application/x-www-form-urlencoded; charset=UTF-8"); + } else { + logger.debug("Setting cookie with username {} and vid {}", userName, password); + request.cookie(new HttpCookie(USER_NAME, userName)); + request.cookie(new HttpCookie(PASSWORD_NAME, password)); + } + logger.debug("HTTP POST Request {}.", request.toString()); + return request.send(); + } + + private T postJSONVerisureAPI(String url, String data, Class jsonClass) + throws ExecutionException, InterruptedException, TimeoutException, JsonSyntaxException, PostToAPIException { + for (int cnt = 0; cnt < APISERVERLIST.size(); cnt++) { + ContentResponse response = postVerisureAPI(apiServerInUse + url, data, Boolean.TRUE); + logger.debug("HTTP Response ({})", response.getStatus()); + if (response.getStatus() == HttpStatus.OK_200) { + String content = response.getContentAsString(); + if (content.contains("\"message\":\"Request Failed") && content.contains("503")) { + // Maybe Verisure has switched API server in use? + logger.debug("Changed API server! Response: {}", content); + setApiServerInUse(getNextApiServer()); + } else { + String contentChomped = content.trim(); + logger.trace("Response body: {}", content); + return gson.fromJson(contentChomped, jsonClass); + } + } else { + logger.debug("Failed to send POST, Http status code: {}", response.getStatus()); + } + } + throw new PostToAPIException("Failed to POST to API"); + } + + private int postVerisureAPI(String urlString, String data) { + String url; + if (urlString.contains("https://mypages")) { + url = urlString; + } else { + url = apiServerInUse + urlString; + } + + for (int cnt = 0; cnt < APISERVERLIST.size(); cnt++) { + try { + ContentResponse response = postVerisureAPI(url, data, Boolean.FALSE); + logger.debug("HTTP Response ({})", response.getStatus()); + int httpStatus = response.getStatus(); + if (httpStatus == HttpStatus.OK_200) { + String content = response.getContentAsString(); + if (content.contains("\"message\":\"Request Failed. Code 503 from")) { + if (url.contains("https://mypages")) { + // Not an API URL + return HttpStatus.SERVICE_UNAVAILABLE_503; + } else { + // Maybe Verisure has switched API server in use + setApiServerInUse(getNextApiServer()); + url = apiServerInUse + urlString; + } + } else { + logTraceWithPattern(httpStatus, content); + return httpStatus; + } + } else { + logger.debug("Failed to send POST, Http status code: {}", response.getStatus()); + } + } catch (ExecutionException | InterruptedException | TimeoutException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + return HttpStatus.SERVICE_UNAVAILABLE_503; + } + + private int setSessionCookieAuthLogin() throws ExecutionException, InterruptedException, TimeoutException { + // URL to set status which will give us 2 cookies with username and password used for the session + String url = STATUS; + ContentResponse response = httpClient.GET(url); + logTraceWithPattern(response.getStatus(), response.getContentAsString()); + + url = AUTH_LOGIN; + return postVerisureAPI(url, "empty"); + } + + private boolean getInstallations() { + int httpResultCode = 0; + + try { + httpResultCode = setSessionCookieAuthLogin(); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + logger.warn("Failed to set session cookie {}", e.getMessage()); + return false; + } + + if (httpResultCode == HttpStatus.OK_200) { + String url = START_GRAPHQL; + + String queryQLAccountInstallations = "[{\"operationName\":\"AccountInstallations\",\"variables\":{\"email\":\"" + + userName + + "\"},\"query\":\"query AccountInstallations($email: String!) {\\n account(email: $email) {\\n owainstallations {\\n giid\\n alias\\n type\\n subsidiary\\n dealerId\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]"; + try { + VerisureInstallationsDTO installations = postJSONVerisureAPI(url, queryQLAccountInstallations, + VerisureInstallationsDTO.class); + logger.debug("Installation: {}", installations.toString()); + List owaInstList = installations.getData().getAccount().getOwainstallations(); + boolean pinCodesMatchInstallations = true; + List pinCodes = null; + String pinCode = this.pinCode; + if (pinCode != null) { + pinCodes = Arrays.asList(pinCode.split(",")); + if (owaInstList.size() != pinCodes.size()) { + logger.debug("Number of installations {} does not match number of pin codes configured {}", + owaInstList.size(), pinCodes.size()); + pinCodesMatchInstallations = false; + } + } else { + logger.debug("No pin-code defined for user {}", userName); + } + + for (int i = 0; i < owaInstList.size(); i++) { + VerisureInstallation vInst = new VerisureInstallation(); + Owainstallation owaInstallation = owaInstList.get(i); + String installationId = owaInstallation.getGiid(); + if (owaInstallation.getAlias() != null && installationId != null) { + vInst.setInstallationId(new BigDecimal(installationId)); + vInst.setInstallationName(owaInstallation.getAlias()); + if (pinCode != null && pinCodes != null) { + int pinCodeIndex = pinCodesMatchInstallations ? i : 0; + vInst.setPinCode(pinCodes.get(pinCodeIndex)); + logger.debug("Setting configured pincode index[{}] to installation ID {}", pinCodeIndex, + installationId); + } + verisureInstallations.put(new BigDecimal(installationId), vInst); + } else { + logger.warn("Failed to get alias and/or giid"); + return false; + } + } + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } else { + logger.warn("Failed to set session cookie and auth login, HTTP result code: {}", httpResultCode); + return false; + } + return true; + } + + private synchronized boolean logIn() { + try { + if (!areWeLoggedIn()) { + logger.debug("Attempting to log in to mypages.verisure.com"); + String url = LOGON_SUF; + logger.debug("Login URL: {}", url); + int httpStatusCode = postVerisureAPI(url, authstring); + if (httpStatusCode != HttpStatus.OK_200) { + logger.debug("Failed to login, HTTP status code: {}", httpStatusCode); + return false; + } + return true; + } else { + return true; + } + } catch (ExecutionException | InterruptedException | TimeoutException e) { + logger.warn("Failed to login {}", e.getMessage()); + } + return false; + } + + private void notifyListeners(T thing) { + deviceStatusListeners.forEach(listener -> { + if (listener.getVerisureThingClass().equals(thing.getClass())) { + listener.onDeviceStateChanged(thing); + } + }); + } + + private void notifyListenersIfChanged(VerisureThingDTO thing, VerisureInstallation installation, String deviceId) { + String normalizedDeviceId = VerisureThingConfiguration.normalizeDeviceId(deviceId); + thing.setDeviceId(normalizedDeviceId); + thing.setSiteId(installation.getInstallationId()); + thing.setSiteName(installation.getInstallationName()); + VerisureThingDTO oldObj = verisureThings.get(normalizedDeviceId); + if (!thing.equals(oldObj)) { + verisureThings.put(thing.getDeviceId(), thing); + notifyListeners(thing); + } else { + logger.trace("No need to notify listeners for thing: {}", thing); + } + } + + private void updateStatus() { + logger.debug("Update status"); + verisureInstallations.forEach((installationId, installation) -> { + try { + configureInstallationInstance(installation.getInstallationId()); + int httpResultCode = setSessionCookieAuthLogin(); + if (httpResultCode == HttpStatus.OK_200) { + updateAlarmStatus(installation); + updateSmartLockStatus(installation); + updateMiceDetectionStatus(installation); + updateClimateStatus(installation); + updateDoorWindowStatus(installation); + updateUserPresenceStatus(installation); + updateSmartPlugStatus(installation); + updateBroadbandConnectionStatus(installation); + updateEventLogStatus(installation); + updateGatewayStatus(installation); + } else { + logger.debug("Failed to set session cookie and auth login, HTTP result code: {}", httpResultCode); + } + } catch (ExecutionException | InterruptedException | TimeoutException e) { + logger.debug("Failed to update status {}", e.getMessage()); + } + }); + } + + private String createOperationJSON(String operation, VariablesDTO variables, String query) { + OperationDTO operationJSON = new OperationDTO(); + operationJSON.setOperationName(operation); + operationJSON.setVariables(variables); + operationJSON.setQuery(query); + return gson.toJson(Collections.singletonList(operationJSON)); + } + + private synchronized void updateAlarmStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "ArmState"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n armState {\n type\n statusType\n date\n name\n changedVia\n allowedForFirstLine\n allowed\n errorCodes {\n value\n message\n __typename\n}\n __typename\n}\n __typename\n}\n}\n"; + + String queryQLAlarmStatus = createOperationJSON(operation, variables, query); + logger.debug("Quering API for alarm status!"); + try { + VerisureThingDTO thing = postJSONVerisureAPI(url, queryQLAlarmStatus, VerisureAlarmsDTO.class); + logger.debug("REST Response ({})", thing); + // Set unique deviceID + String deviceId = "alarm" + installationId; + thing.setDeviceId(deviceId); + notifyListenersIfChanged(thing, installation, deviceId); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateSmartLockStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "DoorLock"; + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n doorlocks {\n device {\n deviceLabel\n area\n __typename\n}\n currentLockState\n eventTime\n secureModeActive\n motorJam\n userString\n method\n __typename\n}\n __typename\n}\n}\n"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String queryQLSmartLock = createOperationJSON(operation, variables, query); + logger.debug("Quering API for smart lock status"); + + try { + VerisureSmartLocksDTO thing = postJSONVerisureAPI(url, queryQLSmartLock, VerisureSmartLocksDTO.class); + logger.debug("REST Response ({})", thing); + List doorLockList = thing.getData().getInstallation().getDoorlocks(); + doorLockList.forEach(doorLock -> { + VerisureSmartLocksDTO slThing = new VerisureSmartLocksDTO(); + VerisureSmartLocksDTO.Installation inst = new VerisureSmartLocksDTO.Installation(); + inst.setDoorlocks(Collections.singletonList(doorLock)); + VerisureSmartLocksDTO.Data data = new VerisureSmartLocksDTO.Data(); + data.setInstallation(inst); + slThing.setData(data); + // Set unique deviceID + String deviceId = doorLock.getDevice().getDeviceLabel(); + if (deviceId != null) { + // Set location + slThing.setLocation(doorLock.getDevice().getArea()); + slThing.setDeviceId(deviceId); + // Fetch more info from old endpoint + try { + VerisureSmartLockDTO smartLockThing = getJSONVerisureAPI(SMARTLOCK_PATH + slThing.getDeviceId(), + VerisureSmartLockDTO.class); + logger.debug("REST Response ({})", smartLockThing); + slThing.setSmartLockJSON(smartLockThing); + notifyListenersIfChanged(slThing, installation, deviceId); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException e) { + logger.warn("Failed to query for smartlock status: {}", e.getMessage()); + } + } + }); + + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateSmartPlugStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "SmartPlug"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n smartplugs {\n device {\n deviceLabel\n area\n gui {\n support\n label\n __typename\n}\n __typename\n}\n currentState\n icon\n isHazardous\n __typename\n}\n __typename\n}\n}\n"; + String queryQLSmartPlug = createOperationJSON(operation, variables, query); + logger.debug("Quering API for smart plug status"); + + try { + VerisureSmartPlugsDTO thing = postJSONVerisureAPI(url, queryQLSmartPlug, VerisureSmartPlugsDTO.class); + logger.debug("REST Response ({})", thing); + List smartPlugList = thing.getData().getInstallation().getSmartplugs(); + smartPlugList.forEach(smartPlug -> { + VerisureSmartPlugsDTO spThing = new VerisureSmartPlugsDTO(); + VerisureSmartPlugsDTO.Installation inst = new VerisureSmartPlugsDTO.Installation(); + inst.setSmartplugs(Collections.singletonList(smartPlug)); + VerisureSmartPlugsDTO.Data data = new VerisureSmartPlugsDTO.Data(); + data.setInstallation(inst); + spThing.setData(data); + // Set unique deviceID + String deviceId = smartPlug.getDevice().getDeviceLabel(); + if (deviceId != null) { + // Set location + spThing.setLocation(smartPlug.getDevice().getArea()); + notifyListenersIfChanged(spThing, installation, deviceId); + } + }); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateClimateStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String operation = "Climate"; + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n climates {\n device {\n deviceLabel\n area\n gui {\n label\n __typename\n }\n __typename\n }\n humidityEnabled\n humidityTimestamp\n humidityValue\n temperatureTimestamp\n temperatureValue\n __typename\n }\n __typename\n}\n}\n"; + + String queryQLClimates = createOperationJSON(operation, variables, query); + logger.debug("Quering API for climate status"); + + try { + VerisureClimatesDTO thing = postJSONVerisureAPI(url, queryQLClimates, VerisureClimatesDTO.class); + logger.debug("REST Response ({})", thing); + List climateList = thing.getData().getInstallation().getClimates(); + climateList.forEach(climate -> { + // If thing is Mouse detection device, then skip it, but fetch temperature from it + String type = climate.getDevice().getGui().getLabel(); + if ("MOUSE".equals(type)) { + logger.debug("Mouse detection device!"); + String deviceId = climate.getDevice().getDeviceLabel(); + if (deviceId != null) { + deviceId = VerisureThingConfiguration.normalizeDeviceId(deviceId); + VerisureThingDTO mouseThing = verisureThings.get(deviceId); + if (mouseThing != null && mouseThing instanceof VerisureMiceDetectionDTO) { + VerisureMiceDetectionDTO miceDetectorThing = (VerisureMiceDetectionDTO) mouseThing; + miceDetectorThing.setTemperatureValue(climate.getTemperatureValue()); + miceDetectorThing.setTemperatureTime(climate.getTemperatureTimestamp()); + notifyListeners(miceDetectorThing); + logger.debug("Found climate thing for a Verisure Mouse Detector"); + } + } + return; + } + VerisureClimatesDTO cThing = new VerisureClimatesDTO(); + VerisureClimatesDTO.Installation inst = new VerisureClimatesDTO.Installation(); + inst.setClimates(Collections.singletonList(climate)); + VerisureClimatesDTO.Data data = new VerisureClimatesDTO.Data(); + data.setInstallation(inst); + cThing.setData(data); + // Set unique deviceID + String deviceId = climate.getDevice().getDeviceLabel(); + if (deviceId != null) { + // Set location + cThing.setLocation(climate.getDevice().getArea()); + notifyListenersIfChanged(cThing, installation, deviceId); + } + }); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateDoorWindowStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "DoorWindow"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n doorWindows {\n device {\n deviceLabel\n area\n __typename\n }\n type\n state\n wired\n reportTime\n __typename\n }\n __typename\n}\n}\n"; + + String queryQLDoorWindow = createOperationJSON(operation, variables, query); + logger.debug("Quering API for door&window status"); + + try { + VerisureDoorWindowsDTO thing = postJSONVerisureAPI(url, queryQLDoorWindow, VerisureDoorWindowsDTO.class); + logger.debug("REST Response ({})", thing); + List doorWindowList = thing.getData().getInstallation().getDoorWindows(); + doorWindowList.forEach(doorWindow -> { + VerisureDoorWindowsDTO dThing = new VerisureDoorWindowsDTO(); + VerisureDoorWindowsDTO.Installation inst = new VerisureDoorWindowsDTO.Installation(); + inst.setDoorWindows(Collections.singletonList(doorWindow)); + VerisureDoorWindowsDTO.Data data = new VerisureDoorWindowsDTO.Data(); + data.setInstallation(inst); + dThing.setData(data); + // Set unique deviceID + String deviceId = doorWindow.getDevice().getDeviceLabel(); + if (deviceId != null) { + // Set location + dThing.setLocation(doorWindow.getDevice().getArea()); + notifyListenersIfChanged(dThing, installation, deviceId); + } + }); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateBroadbandConnectionStatus(VerisureInstallation inst) { + BigDecimal installationId = inst.getInstallationId(); + String url = START_GRAPHQL; + String operation = "Broadband"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n broadband {\n testDate\n isBroadbandConnected\n __typename\n }\n __typename\n}\n}\n"; + + String queryQLBroadbandConnection = createOperationJSON(operation, variables, query); + logger.debug("Quering API for broadband connection status"); + + try { + VerisureThingDTO thing = postJSONVerisureAPI(url, queryQLBroadbandConnection, + VerisureBroadbandConnectionsDTO.class); + logger.debug("REST Response ({})", thing); + // Set unique deviceID + String deviceId = "bc" + installationId; + notifyListenersIfChanged(thing, inst, deviceId); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateUserPresenceStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "userTrackings"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String query = "query " + operation + + "($giid: String!) {\ninstallation(giid: $giid) {\n userTrackings {\n isCallingUser\n webAccount\n status\n xbnContactId\n currentLocationName\n deviceId\n name\n currentLocationTimestamp\n deviceName\n currentLocationId\n __typename\n}\n __typename\n}\n}\n"; + + String queryQLUserPresence = createOperationJSON(operation, variables, query); + logger.debug("Quering API for user presence status"); + + try { + VerisureUserPresencesDTO thing = postJSONVerisureAPI(url, queryQLUserPresence, + VerisureUserPresencesDTO.class); + logger.debug("REST Response ({})", thing); + List userTrackingList = thing.getData().getInstallation() + .getUserTrackings(); + userTrackingList.forEach(userTracking -> { + String localUserTrackingStatus = userTracking.getStatus(); + if (localUserTrackingStatus != null && localUserTrackingStatus.equals("ACTIVE")) { + VerisureUserPresencesDTO upThing = new VerisureUserPresencesDTO(); + VerisureUserPresencesDTO.Installation inst = new VerisureUserPresencesDTO.Installation(); + inst.setUserTrackings(Collections.singletonList(userTracking)); + VerisureUserPresencesDTO.Data data = new VerisureUserPresencesDTO.Data(); + data.setInstallation(inst); + upThing.setData(data); + // Set unique deviceID + String deviceId = "up" + userTracking.getWebAccount() + installationId; + notifyListenersIfChanged(upThing, installation, deviceId); + } + }); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateMiceDetectionStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "Mouse"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n mice {\n device {\n deviceLabel\n area\n gui {\n support\n __typename\n}\n __typename\n}\n type\n detections {\n count\n gatewayTime\n nodeTime\n duration\n __typename\n}\n __typename\n}\n __typename\n}\n}\n"; + + String queryQLMiceDetection = createOperationJSON(operation, variables, query); + logger.debug("Quering API for mice detection status"); + + try { + VerisureMiceDetectionDTO thing = postJSONVerisureAPI(url, queryQLMiceDetection, + VerisureMiceDetectionDTO.class); + logger.debug("REST Response ({})", thing); + List miceList = thing.getData().getInstallation().getMice(); + miceList.forEach(mouse -> { + VerisureMiceDetectionDTO miceThing = new VerisureMiceDetectionDTO(); + VerisureMiceDetectionDTO.Installation inst = new VerisureMiceDetectionDTO.Installation(); + inst.setMice(Collections.singletonList(mouse)); + VerisureMiceDetectionDTO.Data data = new VerisureMiceDetectionDTO.Data(); + data.setInstallation(inst); + miceThing.setData(data); + // Set unique deviceID + String deviceId = mouse.getDevice().getDeviceLabel(); + logger.debug("Mouse id: {} for thing: {}", deviceId, mouse); + if (deviceId != null) { + // Set location + miceThing.setLocation(mouse.getDevice().getArea()); + notifyListenersIfChanged(miceThing, installation, deviceId); + } + }); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateEventLogStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "EventLog"; + int offset = 0; + int numberOfEvents = this.numberOfEvents; + List eventCategories = new ArrayList<>(Arrays.asList("INTRUSION", "FIRE", "SOS", "WATER", "ANIMAL", + "TECHNICAL", "WARNING", "ARM", "DISARM", "LOCK", "UNLOCK", "PICTURE", "CLIMATE", "CAMERA_SETTINGS", + "DOORWINDOW_STATE_OPENED", "DOORWINDOW_STATE_CLOSED", "USERTRACKING")); + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + variables.setHideNotifications(true); + variables.setOffset(offset); + variables.setPagesize(numberOfEvents); + variables.setEventCategories(eventCategories); + String query = "query " + operation + + "($giid: String!, $offset: Int!, $pagesize: Int!, $eventCategories: [String], $fromDate: String, $toDate: String, $eventContactIds: [String]) {\n installation(giid: $giid) {\n eventLog(offset: $offset, pagesize: $pagesize, eventCategories: $eventCategories, eventContactIds: $eventContactIds, fromDate: $fromDate, toDate: $toDate) {\n moreDataAvailable\n pagedList {\n device {\n deviceLabel\n area\n gui {\n label\n __typename\n }\n __typename\n }\n gatewayArea\n eventType\n eventCategory\n eventSource\n eventId\n eventTime\n userName\n armState\n userType\n climateValue\n sensorType\n eventCount\n __typename\n }\n __typename\n }\n __typename\n }\n}\n"; + + String queryQLEventLog = createOperationJSON(operation, variables, query); + logger.debug("Quering API for event log status"); + + try { + VerisureEventLogDTO thing = postJSONVerisureAPI(url, queryQLEventLog, VerisureEventLogDTO.class); + logger.debug("REST Response ({})", thing); + // Set unique deviceID + String deviceId = "el" + installationId; + notifyListenersIfChanged(thing, installation, deviceId); + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private synchronized void updateGatewayStatus(VerisureInstallation installation) { + BigDecimal installationId = installation.getInstallationId(); + String url = START_GRAPHQL; + String operation = "communicationState"; + VariablesDTO variables = new VariablesDTO(); + variables.setGiid(installationId.toString()); + + String query = "query " + operation + + "($giid: String!) {\n installation(giid: $giid) {\n communicationState {\n hardwareCarrierType\n result\n mediaType\n device {\n deviceLabel\n area\n gui {\n label\n __typename\n }\n __typename\n }\n testDate\n __typename\n }\n __typename\n }\n}"; + + String queryQLEventLog = createOperationJSON(operation, variables, query); + logger.debug("Quering API for gateway status"); + + try { + VerisureGatewayDTO thing = postJSONVerisureAPI(url, queryQLEventLog, VerisureGatewayDTO.class); + logger.debug("REST Response ({})", thing); + // Set unique deviceID + List communicationStateList = thing.getData().getInstallation().getCommunicationState(); + if (!communicationStateList.isEmpty()) { + String deviceId = communicationStateList.get(0).getDevice().getDeviceLabel(); + if (deviceId != null) { + notifyListenersIfChanged(thing, installation, deviceId); + } + } + } catch (ExecutionException | InterruptedException | TimeoutException | JsonSyntaxException + | PostToAPIException e) { + logger.warn("Failed to send a POST to the API {}", e.getMessage()); + } + } + + private final class VerisureInstallation { + private @Nullable String installationName; + private BigDecimal installationId = BigDecimal.ZERO; + private @Nullable String pinCode; + + public @Nullable String getPinCode() { + return pinCode; + } + + public void setPinCode(@Nullable String pinCode) { + this.pinCode = pinCode; + } + + public VerisureInstallation() { + } + + public BigDecimal getInstallationId() { + return installationId; + } + + public @Nullable String getInstallationName() { + return installationName; + } + + public void setInstallationId(BigDecimal installationId) { + this.installationId = installationId; + } + + public void setInstallationName(@Nullable String installationName) { + this.installationName = installationName; + } + } + + private static class OperationDTO { + + @SuppressWarnings("unused") + private @Nullable String operationName; + @SuppressWarnings("unused") + private VariablesDTO variables = new VariablesDTO(); + @SuppressWarnings("unused") + private @Nullable String query; + + public void setOperationName(String operationName) { + this.operationName = operationName; + } + + public void setVariables(VariablesDTO variables) { + this.variables = variables; + } + + public void setQuery(String query) { + this.query = query; + } + } + + public static class VariablesDTO { + + @SuppressWarnings("unused") + private boolean hideNotifications; + @SuppressWarnings("unused") + private int offset; + @SuppressWarnings("unused") + private int pagesize; + @SuppressWarnings("unused") + private @Nullable List eventCategories = null; + @SuppressWarnings("unused") + private @Nullable String giid; + + public void setHideNotifications(boolean hideNotifications) { + this.hideNotifications = hideNotifications; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public void setPagesize(int pagesize) { + this.pagesize = pagesize; + } + + public void setEventCategories(List eventCategories) { + this.eventCategories = eventCategories; + } + + public void setGiid(String giid) { + this.giid = giid; + } + } + + private class PostToAPIException extends Exception { + + private static final long serialVersionUID = 1L; + + public PostToAPIException(String message) { + super(message); + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureThingConfiguration.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureThingConfiguration.java new file mode 100644 index 0000000000000..64c537cecb009 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/VerisureThingConfiguration.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Configuration class for VerisureThingHandler. + * + * + * @author Jan Gustafsson - Initial contribution + */ +@NonNullByDefault +public class VerisureThingConfiguration { + public static final String DEVICE_ID_LABEL = "deviceId"; + + private String deviceId = ""; + private int numberOfEvents; + private int eventTriggerDelay; + + public String getDeviceId() { + // Make sure device id is normalized, i.e. replace all non character/digits with empty string + return normalizeDeviceId(deviceId); + } + + public static String normalizeDeviceId(String unnormalizedDeviceId) { + return unnormalizedDeviceId.replaceAll("[^a-zA-Z0-9]+", ""); + } + + public int getNumberOfEvents() { + return numberOfEvents; + } + + public int getEventTriggerDelay() { + return eventTriggerDelay; + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java new file mode 100644 index 0000000000000..4a02e79cd4400 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/discovery/VerisureThingDiscoveryService.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.discovery; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.openhab.binding.verisure.internal.VerisureHandlerFactory; +import org.openhab.binding.verisure.internal.VerisureSession; +import org.openhab.binding.verisure.internal.VerisureThingConfiguration; +import org.openhab.binding.verisure.internal.dto.VerisureThingDTO; +import org.openhab.binding.verisure.internal.handler.VerisureBridgeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The discovery service, notified by a listener on the VerisureSession. + * + * @author Jarle Hjortland - Initial contribution + * @author Jan Gustafsson - Further development + * + */ +@NonNullByDefault +public class VerisureThingDiscoveryService extends AbstractDiscoveryService + implements DiscoveryService, ThingHandlerService { + + private static final int SEARCH_TIME_SECONDS = 60; + private final Logger logger = LoggerFactory.getLogger(VerisureThingDiscoveryService.class); + + private @NonNullByDefault({}) VerisureBridgeHandler verisureBridgeHandler; + private @NonNullByDefault({}) ThingUID bridgeUID; + + public VerisureThingDiscoveryService() { + super(VerisureHandlerFactory.SUPPORTED_THING_TYPES, SEARCH_TIME_SECONDS); + } + + @Override + public void startScan() { + logger.debug("VerisureThingDiscoveryService:startScan"); + removeOlderResults(getTimestampOfLastScan()); + if (verisureBridgeHandler != null) { + VerisureSession session = verisureBridgeHandler.getSession(); + if (session != null) { + Collection verisureThings = session.getVerisureThings(); + verisureThings.stream().forEach(thing -> { + logger.debug("Discovered thing: {}", thing); + onThingAddedInternal(thing); + }); + } + } + } + + private void onThingAddedInternal(VerisureThingDTO thing) { + logger.debug("VerisureThingDiscoveryService:OnThingAddedInternal"); + ThingUID thingUID = getThingUID(thing); + String deviceId = thing.getDeviceId(); + if (thingUID != null) { + if (verisureBridgeHandler != null) { + String label = "Device Id: " + deviceId; + if (thing.getLocation() != null) { + label += ", Location: " + thing.getLocation(); + } + if (thing.getSiteName() != null) { + label += ", Site name: " + thing.getSiteName(); + } + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID) + .withLabel(label).withProperty(VerisureThingConfiguration.DEVICE_ID_LABEL, deviceId) + .withRepresentationProperty(deviceId).build(); + logger.debug("thinguid: {}, bridge {}, label {}", thingUID, bridgeUID, deviceId); + thingDiscovered(discoveryResult); + } + } else { + logger.debug("Discovered unsupported thing of type '{}' with deviceId {}", thing.getClass(), deviceId); + } + } + + private @Nullable ThingUID getThingUID(VerisureThingDTO thing) { + ThingUID thingUID = null; + if (verisureBridgeHandler != null) { + String deviceId = thing.getDeviceId(); + // Make sure device id is normalized, i.e. replace all non character/digits with empty string + deviceId = VerisureThingConfiguration.normalizeDeviceId(deviceId); + thingUID = new ThingUID(thing.getThingTypeUID(), bridgeUID, deviceId); + } + return thingUID; + } + + @Override + public void activate() { + super.activate(Collections.singletonMap(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY, true)); + } + + @Override + public void deactivate() { + super.deactivate(); + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof VerisureBridgeHandler) { + verisureBridgeHandler = (VerisureBridgeHandler) handler; + bridgeUID = verisureBridgeHandler.getUID(); + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return verisureBridgeHandler; + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureAlarmsDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureAlarmsDTO.java new file mode 100644 index 0000000000000..7d6ab0a659920 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureAlarmsDTO.java @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_ALARM; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The alarms of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureAlarmsDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_ALARM; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class ArmState { + + private @Nullable String type; + private @Nullable String statusType; + private @Nullable String date; + private @Nullable String name; + private @Nullable String changedVia; + private boolean allowedForFirstLine; + private boolean allowed; + private List errorCodes = new ArrayList<>(); + private @Nullable String typename; + + public @Nullable String getType() { + return type; + } + + public @Nullable String getStatusType() { + return statusType; + } + + public void setStatusType(@Nullable String statusType) { + this.statusType = statusType; + } + + public @Nullable String getDate() { + return date; + } + + public @Nullable String getName() { + return name; + } + + public @Nullable String getChangedVia() { + return changedVia; + } + + public boolean getAllowedForFirstLine() { + return allowedForFirstLine; + } + + public boolean getAllowed() { + return allowed; + } + + public List getErrorCodes() { + return errorCodes; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (allowed ? 1231 : 1237); + result = prime * result + (allowedForFirstLine ? 1231 : 1237); + String localChangedVia = changedVia; + result = prime * result + ((localChangedVia == null) ? 0 : localChangedVia.hashCode()); + String localDate = date; + result = prime * result + ((localDate == null) ? 0 : localDate.hashCode()); + result = prime * result + errorCodes.hashCode(); + String localName = name; + result = prime * result + ((localName == null) ? 0 : localName.hashCode()); + String localStatusType = statusType; + result = prime * result + ((localStatusType == null) ? 0 : localStatusType.hashCode()); + String localType = type; + result = prime * result + ((localType == null) ? 0 : localType.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ArmState other = (ArmState) obj; + if (allowed != other.allowed) { + return false; + } + if (allowedForFirstLine != other.allowedForFirstLine) { + return false; + } + String localChangedVia = changedVia; + if (localChangedVia == null) { + if (other.changedVia != null) { + return false; + } + } else if (!localChangedVia.equals(other.changedVia)) { + return false; + } + String localdate = date; + if (localdate == null) { + if (other.date != null) { + return false; + } + } else if (!localdate.equals(other.date)) { + return false; + } + if (!errorCodes.equals(other.errorCodes)) { + return false; + } + String localName = name; + if (localName == null) { + if (other.name != null) { + return false; + } + } else if (!localName.equals(other.name)) { + return false; + } + String localStatusType = statusType; + if (localStatusType == null) { + if (other.statusType != null) { + return false; + } + } else if (!localStatusType.equals(other.statusType)) { + return false; + } + String localType = type; + if (localType == null) { + if (other.type != null) { + return false; + } + } else if (!localType.equals(other.type)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "ArmState [type=" + type + ", statusType=" + statusType + ", date=" + date + ", name=" + name + + ", changedVia=" + changedVia + ", allowedForFirstLine=" + allowedForFirstLine + ", allowed=" + + allowed + ", errorCodes=" + errorCodes + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureBaseThingDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureBaseThingDTO.java new file mode 100644 index 0000000000000..88df7284ae3d9 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureBaseThingDTO.java @@ -0,0 +1,571 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.openhab.binding.verisure.internal.VerisureThingConfiguration; +import org.openhab.binding.verisure.internal.dto.VerisureAlarmsDTO.ArmState; +import org.openhab.binding.verisure.internal.dto.VerisureBroadbandConnectionsDTO.Broadband; +import org.openhab.binding.verisure.internal.dto.VerisureClimatesDTO.Climate; +import org.openhab.binding.verisure.internal.dto.VerisureDoorWindowsDTO.DoorWindow; +import org.openhab.binding.verisure.internal.dto.VerisureEventLogDTO.EventLog; +import org.openhab.binding.verisure.internal.dto.VerisureGatewayDTO.CommunicationState; +import org.openhab.binding.verisure.internal.dto.VerisureMiceDetectionDTO.Mouse; +import org.openhab.binding.verisure.internal.dto.VerisureSmartLocksDTO.Doorlock; +import org.openhab.binding.verisure.internal.dto.VerisureSmartPlugsDTO.Smartplug; +import org.openhab.binding.verisure.internal.dto.VerisureUserPresencesDTO.UserTracking; + +import com.google.gson.annotations.SerializedName; + +/** + * A base JSON thing for other Verisure things to inherit from. + * + * @author Jarle Hjortland - Initial contribution + * + */ +@NonNullByDefault +public abstract class VerisureBaseThingDTO implements VerisureThingDTO { + + protected String deviceId = ""; + protected @Nullable String name; + protected @Nullable String location; + protected @Nullable String status; + protected @Nullable String siteName; + protected BigDecimal siteId = BigDecimal.ZERO; + protected Data data = new Data(); + + public @Nullable String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public @Nullable String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDeviceId() { + return deviceId; + } + + @Override + public void setDeviceId(String deviceId) { + // Make sure device id is normalized + this.deviceId = VerisureThingConfiguration.normalizeDeviceId(deviceId); + } + + @Override + public @Nullable String getLocation() { + return location; + } + + public void setLocation(@Nullable String location) { + this.location = location; + } + + @Override + public @Nullable String getSiteName() { + return siteName; + } + + @Override + public void setSiteName(@Nullable String siteName) { + this.siteName = siteName; + } + + @Override + public BigDecimal getSiteId() { + return siteId; + } + + @Override + public void setSiteId(BigDecimal siteId) { + this.siteId = siteId; + } + + @Override + public abstract ThingTypeUID getThingTypeUID(); + + public Data getData() { + return data; + } + + public void setData(Data data) { + this.data = data; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + data.hashCode(); + result = prime * result + deviceId.hashCode(); + String localLocation = location; + result = prime * result + ((localLocation == null) ? 0 : localLocation.hashCode()); + String localName = name; + result = prime * result + ((localName == null) ? 0 : localName.hashCode()); + result = prime * result + siteId.hashCode(); + String localSiteName = siteName; + result = prime * result + ((localSiteName == null) ? 0 : localSiteName.hashCode()); + String localStatus = status; + result = prime * result + ((localStatus == null) ? 0 : localStatus.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + VerisureBaseThingDTO other = (VerisureBaseThingDTO) obj; + if (!data.equals(other.data)) { + return false; + } + if (!deviceId.equals(other.deviceId)) { + return false; + } + String localLocation = location; + if (localLocation == null) { + if (other.location != null) { + return false; + } + } else if (!localLocation.equals(other.location)) { + return false; + } + String localName = name; + if (localName == null) { + if (other.name != null) { + return false; + } + } else if (!localName.equals(other.name)) { + return false; + } + if (!siteId.equals(other.siteId)) { + return false; + } + String localSiteName = siteName; + if (localSiteName == null) { + if (other.siteName != null) { + return false; + } + } else if (!localSiteName.equals(other.siteName)) { + return false; + } + String localStatus = status; + if (localStatus == null) { + if (other.status != null) { + return false; + } + } else if (!localStatus.equals(other.status)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "VerisureBaseThingDTO [deviceId=" + deviceId + ", name=" + name + ", location=" + location + ", status=" + + status + ", siteName=" + siteName + ", siteId=" + siteId + ", data=" + data + "]"; + } + + public static class Data { + private Installation installation = new Installation(); + + public Installation getInstallation() { + return installation; + } + + public void setInstallation(Installation installation) { + this.installation = installation; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + installation.hashCode(); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Data other = (Data) obj; + if (!installation.equals(other.installation)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Data [installation=" + installation + "]"; + } + } + + public static class Installation { + + private ArmState armState = new ArmState(); + private Broadband broadband = new Broadband(); + private EventLog eventLog = new EventLog(); + private List climates = new ArrayList<>(); + private List doorWindows = new ArrayList<>(); + private List communicationState = new ArrayList<>(); + private List mice = new ArrayList<>(); + private List doorlocks = new ArrayList<>(); + private List smartplugs = new ArrayList<>(); + private List userTrackings = new ArrayList<>(); + + @SerializedName("__typename") + private @Nullable String typename; + + public ArmState getArmState() { + return armState; + } + + public Broadband getBroadband() { + return broadband; + } + + public List getClimates() { + return climates; + } + + public void setClimates(List climates) { + this.climates = climates; + } + + public List getDoorWindows() { + return doorWindows; + } + + public void setDoorWindows(List doorWindows) { + this.doorWindows = doorWindows; + } + + public EventLog getEventLog() { + return eventLog; + } + + public List getCommunicationState() { + return communicationState; + } + + public List getMice() { + return mice; + } + + public void setMice(List mice) { + this.mice = mice; + } + + public List getDoorlocks() { + return doorlocks; + } + + public void setDoorlocks(List doorlocks) { + this.doorlocks = doorlocks; + } + + public List getSmartplugs() { + return smartplugs; + } + + public void setSmartplugs(List smartplugs) { + this.smartplugs = smartplugs; + } + + public List getUserTrackings() { + return userTrackings; + } + + public void setUserTrackings(List userTrackings) { + this.userTrackings = userTrackings; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + armState.hashCode(); + result = prime * result + broadband.hashCode(); + result = prime * result + climates.hashCode(); + result = prime * result + communicationState.hashCode(); + result = prime * result + doorWindows.hashCode(); + result = prime * result + doorlocks.hashCode(); + result = prime * result + eventLog.hashCode(); + result = prime * result + mice.hashCode(); + result = prime * result + smartplugs.hashCode(); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + result = prime * result + userTrackings.hashCode(); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Installation other = (Installation) obj; + if (!armState.equals(other.armState)) { + return false; + } + if (!broadband.equals(other.broadband)) { + return false; + } + if (!climates.equals(other.climates)) { + return false; + } + if (!communicationState.equals(other.communicationState)) { + return false; + } + if (!doorWindows.equals(other.doorWindows)) { + return false; + } + if (!doorlocks.equals(other.doorlocks)) { + return false; + } + if (!eventLog.equals(other.eventLog)) { + return false; + } + if (!mice.equals(other.mice)) { + return false; + } + if (!smartplugs.equals(other.smartplugs)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + if (!userTrackings.equals(other.userTrackings)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Installation [armState=" + armState + ", broadband=" + broadband + ", eventLog=" + eventLog + + ", climates=" + climates + ", doorWindows=" + doorWindows + ", communicationState=" + + communicationState + ", mice=" + mice + ", doorlocks=" + doorlocks + ", smartplugs=" + smartplugs + + ", userTrackings=" + userTrackings + ", typename=" + typename + "]"; + } + } + + public static class Device { + + private @Nullable String deviceLabel; + private @Nullable String area; + private Gui gui = new Gui(); + @SerializedName("__typename") + private @Nullable String typename; + + public @Nullable String getDeviceLabel() { + return deviceLabel; + } + + public @Nullable String getArea() { + return area; + } + + public Gui getGui() { + return gui; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localArea = area; + result = prime * result + ((localArea == null) ? 0 : localArea.hashCode()); + String localDeviceLabel = deviceLabel; + result = prime * result + ((localDeviceLabel == null) ? 0 : localDeviceLabel.hashCode()); + result = prime * result + gui.hashCode(); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Device other = (Device) obj; + String localArea = area; + if (localArea == null) { + if (other.area != null) { + return false; + } + } else if (!localArea.equals(other.area)) { + return false; + } + String localDeviceLabel = deviceLabel; + if (localDeviceLabel == null) { + if (other.deviceLabel != null) { + return false; + } + } else if (!localDeviceLabel.equals(other.deviceLabel)) { + return false; + } + if (!gui.equals(other.gui)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Device [deviceLabel=" + deviceLabel + ", area=" + area + ", gui=" + gui + ", typename=" + typename + + "]"; + } + } + + public static class Gui { + + private @Nullable String label; + private @Nullable String support; + @SerializedName("__typename") + private @Nullable String typename; + + public @Nullable String getLabel() { + return label; + } + + public @Nullable String getSupport() { + return support; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localLabel = label; + result = prime * result + ((localLabel == null) ? 0 : localLabel.hashCode()); + String localSupport = support; + result = prime * result + ((localSupport == null) ? 0 : localSupport.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Gui other = (Gui) obj; + String localLabel = label; + if (localLabel == null) { + if (other.label != null) { + return false; + } + } else if (!localLabel.equals(other.label)) { + return false; + } + String localSupport = support; + if (localSupport == null) { + if (other.support != null) { + return false; + } + } else if (!localSupport.equals(other.support)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Gui [label=" + label + ", support=" + support + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureBroadbandConnectionsDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureBroadbandConnectionsDTO.java new file mode 100644 index 0000000000000..1c118af6d7f08 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureBroadbandConnectionsDTO.java @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_BROADBAND_CONNECTION; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The broadband connections of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureBroadbandConnectionsDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_BROADBAND_CONNECTION; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class Broadband { + + private @Nullable String testDate; + private boolean isBroadbandConnected; + @SerializedName("__typename") + private @Nullable String typename; + + public @Nullable String getTestDate() { + return testDate; + } + + public boolean isBroadbandConnected() { + return isBroadbandConnected; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (isBroadbandConnected ? 1231 : 1237); + String localTestDate = testDate; + result = prime * result + ((localTestDate == null) ? 0 : localTestDate.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Broadband other = (Broadband) obj; + if (isBroadbandConnected != other.isBroadbandConnected) { + return false; + } + String localTestDate = testDate; + if (localTestDate == null) { + if (other.testDate != null) { + return false; + } + } else if (!localTestDate.equals(other.testDate)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Broadband [testDate=" + testDate + ", isBroadbandConnected=" + isBroadbandConnected + ", typename=" + + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureClimatesDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureClimatesDTO.java new file mode 100644 index 0000000000000..7cf7c7111f2ab --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureClimatesDTO.java @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The climate devices of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureClimatesDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + String type = getData().getInstallation().getClimates().get(0).getDevice().getGui().getLabel(); + if ("SMOKE".equals(type)) { + return THING_TYPE_SMOKEDETECTOR; + } else if ("WATER".equals(type)) { + return THING_TYPE_WATERDETECTOR; + } else if ("HOMEPAD".equals(type)) { + return THING_TYPE_NIGHT_CONTROL; + } else if ("SIREN".equals(type)) { + return THING_TYPE_SIREN; + } else { + return THING_TYPE_SMOKEDETECTOR; + } + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class Climate { + + private Device device = new Device(); + private boolean humidityEnabled; + private @Nullable String humidityTimestamp; + private double humidityValue; + private @Nullable String temperatureTimestamp; + private double temperatureValue; + @SerializedName("__typename") + private @Nullable String typename; + + public Device getDevice() { + return device; + } + + public boolean isHumidityEnabled() { + return humidityEnabled; + } + + public @Nullable String getHumidityTimestamp() { + return humidityTimestamp; + } + + public double getHumidityValue() { + return humidityValue; + } + + public @Nullable String getTemperatureTimestamp() { + return temperatureTimestamp; + } + + public double getTemperatureValue() { + return temperatureValue; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + device.hashCode(); + result = prime * result + (humidityEnabled ? 1231 : 1237); + String localHumidityTimestamp = humidityTimestamp; + result = prime * result + ((localHumidityTimestamp == null) ? 0 : localHumidityTimestamp.hashCode()); + long temp; + temp = Double.doubleToLongBits(humidityValue); + result = prime * result + (int) (temp ^ (temp >>> 32)); + String localTemperatureTimestamp = temperatureTimestamp; + result = prime * result + ((localTemperatureTimestamp == null) ? 0 : localTemperatureTimestamp.hashCode()); + temp = Double.doubleToLongBits(temperatureValue); + result = prime * result + (int) (temp ^ (temp >>> 32)); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Climate other = (Climate) obj; + if (!device.equals(other.device)) { + return false; + } + if (humidityEnabled != other.humidityEnabled) { + return false; + } + String localHumidityTimestamp = humidityTimestamp; + if (localHumidityTimestamp == null) { + if (other.humidityTimestamp != null) { + return false; + } + } else if (!localHumidityTimestamp.equals(other.humidityTimestamp)) { + return false; + } + if (Double.doubleToLongBits(humidityValue) != Double.doubleToLongBits(other.humidityValue)) { + return false; + } + String localTemperatureTimestamp = temperatureTimestamp; + if (localTemperatureTimestamp == null) { + if (other.temperatureTimestamp != null) { + return false; + } + } else if (!localTemperatureTimestamp.equals(other.temperatureTimestamp)) { + return false; + } + if (Double.doubleToLongBits(temperatureValue) != Double.doubleToLongBits(other.temperatureValue)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Climate [device=" + device + ", humidityEnabled=" + humidityEnabled + ", humidityTimestamp=" + + humidityTimestamp + ", humidityValue=" + humidityValue + ", temperatureTimestamp=" + + temperatureTimestamp + ", temperatureValue=" + temperatureValue + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureDoorWindowsDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureDoorWindowsDTO.java new file mode 100644 index 0000000000000..acfdf7d4b12bd --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureDoorWindowsDTO.java @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_DOORWINDOW; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The door and window devices of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureDoorWindowsDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_DOORWINDOW; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class DoorWindow { + + private Device device = new Device(); + private @Nullable String type; + private @Nullable String state; + private boolean wired; + private @Nullable String reportTime; + @SerializedName("__typename") + private @Nullable String typename; + + public Device getDevice() { + return device; + } + + public @Nullable String getType() { + return type; + } + + public @Nullable String getState() { + return state; + } + + public boolean getWired() { + return wired; + } + + public @Nullable String getReportTime() { + return reportTime; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + device.hashCode(); + String localReportTime = reportTime; + result = prime * result + ((localReportTime == null) ? 0 : localReportTime.hashCode()); + String localState = state; + result = prime * result + ((localState == null) ? 0 : localState.hashCode()); + String localType = type; + result = prime * result + ((localType == null) ? 0 : localType.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + result = prime * result + (wired ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + DoorWindow other = (DoorWindow) obj; + if (!device.equals(other.device)) { + return false; + } + String localReportTime = reportTime; + if (localReportTime == null) { + if (other.reportTime != null) { + return false; + } + } else if (!localReportTime.equals(other.reportTime)) { + return false; + } + String localState = state; + if (localState == null) { + if (other.state != null) { + return false; + } + } else if (!localState.equals(other.state)) { + return false; + } + String localType = type; + if (localType == null) { + if (other.type != null) { + return false; + } + } else if (!localType.equals(other.type)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + if (wired != other.wired) { + return false; + } + return true; + } + + @Override + public String toString() { + return "DoorWindow [device=" + device + ", type=" + type + ", state=" + state + ", wired=" + wired + + ", reportTime=" + reportTime + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureEventLogDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureEventLogDTO.java new file mode 100644 index 0000000000000..59cdee26ba56a --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureEventLogDTO.java @@ -0,0 +1,347 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_EVENT_LOG; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The event log of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureEventLogDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_EVENT_LOG; + } + + public static class EventLog { + + private boolean moreDataAvailable; + private List pagedList = new ArrayList<>(); + private @Nullable String typename; + + public boolean isMoreDataAvailable() { + return moreDataAvailable; + } + + public List getPagedList() { + return pagedList; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (moreDataAvailable ? 1231 : 1237); + result = prime * result + pagedList.hashCode(); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + EventLog other = (EventLog) obj; + if (moreDataAvailable != other.moreDataAvailable) { + return false; + } + if (!pagedList.equals(other.pagedList)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "EventLog [moreDataAvailable=" + moreDataAvailable + ", pagedList=" + pagedList + ", typename=" + + typename + "]"; + } + } + + public static class PagedList { + + private @Nullable Device device = new Device(); + private @Nullable String gatewayArea; + private @Nullable String eventType; + private @Nullable String eventCategory; + private @Nullable String eventSource; + private @Nullable String eventId; + private @Nullable String eventTime; + private @Nullable String userName; + private @Nullable String armState; + private @Nullable String userType; + private @Nullable String climateValue; + private @Nullable String sensorType; + private @Nullable String eventCount; + private @Nullable String typename; + + public @Nullable Device getDevice() { + return device; + } + + public @Nullable String getGatewayArea() { + return gatewayArea; + } + + public @Nullable String getEventType() { + return eventType; + } + + public @Nullable String getEventCategory() { + return eventCategory; + } + + public @Nullable String getEventSource() { + return eventSource; + } + + public @Nullable String getEventId() { + return eventId; + } + + public @Nullable String getEventTime() { + return eventTime; + } + + public @Nullable String getUserName() { + return userName; + } + + public @Nullable String getArmState() { + return armState; + } + + public @Nullable String getUserType() { + return userType; + } + + public @Nullable String getClimateValue() { + return climateValue; + } + + public @Nullable String getSensorType() { + return sensorType; + } + + public @Nullable String getEventCount() { + return eventCount; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localArmState = armState; + result = prime * result + ((localArmState == null) ? 0 : localArmState.hashCode()); + String localClimateValue = climateValue; + result = prime * result + ((localClimateValue == null) ? 0 : localClimateValue.hashCode()); + Device localDevice = device; + result = prime * result + ((localDevice == null) ? 0 : localDevice.hashCode()); + String localEventCategory = eventCategory; + result = prime * result + ((localEventCategory == null) ? 0 : localEventCategory.hashCode()); + String localEventCount = eventCount; + result = prime * result + ((localEventCount == null) ? 0 : localEventCount.hashCode()); + String localEventId = eventId; + result = prime * result + ((localEventId == null) ? 0 : localEventId.hashCode()); + String localEventSource = eventSource; + result = prime * result + ((localEventSource == null) ? 0 : localEventSource.hashCode()); + String localEventTime = eventTime; + result = prime * result + ((localEventTime == null) ? 0 : localEventTime.hashCode()); + String localEventType = eventType; + result = prime * result + ((localEventType == null) ? 0 : localEventType.hashCode()); + String localGatewayArea = gatewayArea; + result = prime * result + ((localGatewayArea == null) ? 0 : localGatewayArea.hashCode()); + String localSensorType = sensorType; + result = prime * result + ((localSensorType == null) ? 0 : localSensorType.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + String localUserName = userName; + result = prime * result + ((localUserName == null) ? 0 : localUserName.hashCode()); + String localUserType = userType; + result = prime * result + ((localUserType == null) ? 0 : localUserType.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PagedList other = (PagedList) obj; + String localArmState = armState; + if (localArmState == null) { + if (other.armState != null) { + return false; + } + } else if (!localArmState.equals(other.armState)) { + return false; + } + String localClimateValue = climateValue; + if (localClimateValue == null) { + if (other.climateValue != null) { + return false; + } + } else if (!localClimateValue.equals(other.climateValue)) { + return false; + } + Device localDevice = device; + if (localDevice == null) { + if (other.device != null) { + return false; + } + } else if (!localDevice.equals(other.device)) { + return false; + } + String localEventCategory = eventCategory; + if (localEventCategory == null) { + if (other.eventCategory != null) { + return false; + } + } else if (!localEventCategory.equals(other.eventCategory)) { + return false; + } + String localEventCount = eventCount; + if (localEventCount == null) { + if (other.eventCount != null) { + return false; + } + } else if (!localEventCount.equals(other.eventCount)) { + return false; + } + String localEventId = eventId; + if (localEventId == null) { + if (other.eventId != null) { + return false; + } + } else if (!localEventId.equals(other.eventId)) { + return false; + } + String localEventSource = eventSource; + if (localEventSource == null) { + if (other.eventSource != null) { + return false; + } + } else if (!localEventSource.equals(other.eventSource)) { + return false; + } + String localEventTime = eventTime; + if (localEventTime == null) { + if (other.eventTime != null) { + return false; + } + } else if (!localEventTime.equals(other.eventTime)) { + return false; + } + String localEventType = eventType; + if (localEventType == null) { + if (other.eventType != null) { + return false; + } + } else if (!localEventType.equals(other.eventType)) { + return false; + } + String localGatewayArea = gatewayArea; + if (localGatewayArea == null) { + if (other.gatewayArea != null) { + return false; + } + } else if (!localGatewayArea.equals(other.gatewayArea)) { + return false; + } + String localSensorType = sensorType; + if (localSensorType == null) { + if (other.sensorType != null) { + return false; + } + } else if (!localSensorType.equals(other.sensorType)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + String localUserName = userName; + if (localUserName == null) { + if (other.userName != null) { + return false; + } + } else if (!localUserName.equals(other.userName)) { + return false; + } + String localUserType = userType; + if (localUserType == null) { + if (other.userType != null) { + return false; + } + } else if (!localUserType.equals(other.userType)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "PagedList [device=" + device + ", gatewayArea=" + gatewayArea + ", eventType=" + eventType + + ", eventCategory=" + eventCategory + ", eventSource=" + eventSource + ", eventId=" + eventId + + ", eventTime=" + eventTime + ", userName=" + userName + ", armState=" + armState + ", userType=" + + userType + ", climateValue=" + climateValue + ", sensorType=" + sensorType + ", eventCount=" + + eventCount + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureGatewayDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureGatewayDTO.java new file mode 100644 index 0000000000000..9f92fd1549d37 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureGatewayDTO.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_GATEWAY; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The gateway in the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureGatewayDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_GATEWAY; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class CommunicationState { + + private @Nullable String hardwareCarrierType; + private @Nullable String result; + private @Nullable String mediaType; + private Device device = new Device(); + private @Nullable String testDate; + private @Nullable String typename; + + public @Nullable String getHardwareCarrierType() { + return hardwareCarrierType; + } + + public @Nullable String getResult() { + return result; + } + + public @Nullable String getMediaType() { + return mediaType; + } + + public Device getDevice() { + return device; + } + + public @Nullable String getTestDate() { + return testDate; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + device.hashCode(); + String locaHardwareCarrierType = hardwareCarrierType; + result = prime * result + ((locaHardwareCarrierType == null) ? 0 : locaHardwareCarrierType.hashCode()); + String localMediaType = mediaType; + result = prime * result + ((localMediaType == null) ? 0 : localMediaType.hashCode()); + String localResult = this.result; + result = prime * result + ((localResult == null) ? 0 : localResult.hashCode()); + String localTestDate = testDate; + result = prime * result + ((localTestDate == null) ? 0 : localTestDate.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + CommunicationState other = (CommunicationState) obj; + if (!device.equals(other.device)) { + return false; + } + String locaHardwareCarrierType = hardwareCarrierType; + if (locaHardwareCarrierType == null) { + if (other.hardwareCarrierType != null) { + return false; + } + } else if (!locaHardwareCarrierType.equals(other.hardwareCarrierType)) { + return false; + } + String localMediaType = mediaType; + if (localMediaType == null) { + if (other.mediaType != null) { + return false; + } + } else if (!localMediaType.equals(other.mediaType)) { + return false; + } + String localResult = result; + if (localResult == null) { + if (other.result != null) { + return false; + } + } else if (!localResult.equals(other.result)) { + return false; + } + String localTestDate = testDate; + if (localTestDate == null) { + if (other.testDate != null) { + return false; + } + } else if (!localTestDate.equals(other.testDate)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "CommunicationState [hardwareCarrierType=" + hardwareCarrierType + ", result=" + result + + ", mediaType=" + mediaType + ", device=" + device + ", testDate=" + testDate + ", typename=" + + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureInstallationsDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureInstallationsDTO.java new file mode 100644 index 0000000000000..afd55f563bdb4 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureInstallationsDTO.java @@ -0,0 +1,300 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.verisure.internal.dto.VerisureBaseThingDTO.Installation; + +import com.google.gson.annotations.SerializedName; + +/** + * The installation(s) of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureInstallationsDTO { + + private Data data = new Data(); + + public Data getData() { + return data; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + data.hashCode(); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + VerisureInstallationsDTO other = (VerisureInstallationsDTO) obj; + if (!data.equals(other.data)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "VerisureInstallationsDTO [data=" + data + "]"; + } + + public static class Data { + private Installation installation = new Installation(); + private Account account = new Account(); + + public Account getAccount() { + return account; + } + + public Installation getInstallation() { + return installation; + } + + public void setInstallation(Installation installation) { + this.installation = installation; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + account.hashCode(); + result = prime * result + installation.hashCode(); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Data other = (Data) obj; + if (!account.equals(other.account)) { + return false; + } + if (!installation.equals(other.installation)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Data [installation=" + installation + ", account=" + account + "]"; + } + } + + public static class Account { + + @SerializedName("__typename") + private @Nullable String typename; + private List owainstallations = new ArrayList<>(); + + public @Nullable String getTypename() { + return typename; + } + + public List getOwainstallations() { + return owainstallations; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + owainstallations.hashCode(); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Account other = (Account) obj; + if (!owainstallations.equals(other.owainstallations)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Account [typename=" + typename + ", owainstallations=" + owainstallations + "]"; + } + } + + public static class Owainstallation { + + @SerializedName("__typename") + private @Nullable String typename; + private @Nullable String alias; + private @Nullable String dealerId; + private @Nullable String giid; + private @Nullable String subsidiary; + private @Nullable String type; + + public @Nullable String getTypename() { + return typename; + } + + public @Nullable String getAlias() { + return alias; + } + + public @Nullable String getDealerId() { + return dealerId; + } + + public @Nullable String getGiid() { + return giid; + } + + public @Nullable String getSubsidiary() { + return subsidiary; + } + + public @Nullable String getType() { + return type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localAlias = alias; + result = prime * result + ((localAlias == null) ? 0 : localAlias.hashCode()); + String localDealerId = dealerId; + result = prime * result + ((localDealerId == null) ? 0 : localDealerId.hashCode()); + String localGiid = giid; + result = prime * result + ((localGiid == null) ? 0 : localGiid.hashCode()); + String localSubsidiary = subsidiary; + result = prime * result + ((localSubsidiary == null) ? 0 : localSubsidiary.hashCode()); + String localType = type; + result = prime * result + ((localType == null) ? 0 : localType.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Owainstallation other = (Owainstallation) obj; + String localAlias = alias; + if (localAlias == null) { + if (other.alias != null) { + return false; + } + } else if (!localAlias.equals(other.alias)) { + return false; + } + String localDealerId = dealerId; + if (localDealerId == null) { + if (other.dealerId != null) { + return false; + } + } else if (!localDealerId.equals(other.dealerId)) { + return false; + } + String localGiid = giid; + if (localGiid == null) { + if (other.giid != null) { + return false; + } + } else if (!localGiid.equals(other.giid)) { + return false; + } + String localSubsidiary = subsidiary; + if (localSubsidiary == null) { + if (other.subsidiary != null) { + return false; + } + } else if (!localSubsidiary.equals(other.subsidiary)) { + return false; + } + String localType = type; + if (localType == null) { + if (other.type != null) { + return false; + } + } else if (!localType.equals(other.type)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Owainstallation [typename=" + typename + ", alias=" + alias + ", dealerId=" + dealerId + ", giid=" + + giid + ", subsidiary=" + subsidiary + ", type=" + type + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureMiceDetectionDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureMiceDetectionDTO.java new file mode 100644 index 0000000000000..68b7837e7fedd --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureMiceDetectionDTO.java @@ -0,0 +1,279 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_MICE_DETECTION; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The Mice detection status of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureMiceDetectionDTO extends VerisureBaseThingDTO { + + public static final int UNDEFINED = -1; + private double temperatureValue = UNDEFINED; + private @Nullable String temperatureTimestamp; + + public double getTemperatureValue() { + return temperatureValue; + } + + public void setTemperatureValue(double temperatureValue) { + this.temperatureValue = temperatureValue; + } + + public @Nullable String getTemperatureTime() { + return temperatureTimestamp; + } + + public void setTemperatureTime(@Nullable String temperatureTimestamp) { + this.temperatureTimestamp = temperatureTimestamp; + } + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_MICE_DETECTION; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + String localTemperatureTimestamp = temperatureTimestamp; + result = prime * result + ((localTemperatureTimestamp == null) ? 0 : localTemperatureTimestamp.hashCode()); + long temp; + temp = Double.doubleToLongBits(temperatureValue); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + VerisureMiceDetectionDTO other = (VerisureMiceDetectionDTO) obj; + String localTemperatureTimestamp = temperatureTimestamp; + if (localTemperatureTimestamp == null) { + if (other.temperatureTimestamp != null) { + return false; + } + } else if (!localTemperatureTimestamp.equals(other.temperatureTimestamp)) { + return false; + } + if (Double.doubleToLongBits(temperatureValue) != Double.doubleToLongBits(other.temperatureValue)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "VerisureMiceDetectionDTO [temperatureValue=" + temperatureValue + ", temperatureTimestamp=" + + temperatureTimestamp + "]"; + } + + public static class Mouse { + + private Device device = new Device(); + private @Nullable Object type; + private List detections = new ArrayList<>(); + private @Nullable String typename; + + public Device getDevice() { + return device; + } + + public @Nullable Object getType() { + return type; + } + + public List getDetections() { + return detections; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + detections.hashCode(); + result = prime * result + device.hashCode(); + Object localType = type; + result = prime * result + ((localType == null) ? 0 : localType.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Mouse other = (Mouse) obj; + if (!detections.equals(other.detections)) { + return false; + } + if (!device.equals(other.device)) { + return false; + } + Object localType = type; + if (localType == null) { + if (other.type != null) { + return false; + } + } else if (!localType.equals(other.type)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Mouse [device=" + device + ", type=" + type + ", detections=" + detections + ", typename=" + + typename + "]"; + } + } + + public static class Detection { + + private int count; + private @Nullable String gatewayTime; + private @Nullable String nodeTime; + private int duration; + private @Nullable String typename; + + public int getCount() { + return count; + } + + public @Nullable String getGatewayTime() { + return gatewayTime; + } + + public @Nullable String getNodeTime() { + return nodeTime; + } + + public int getDuration() { + return duration; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + count; + result = prime * result + duration; + String localGatewayTime = gatewayTime; + result = prime * result + ((localGatewayTime == null) ? 0 : localGatewayTime.hashCode()); + String localNodeTime = nodeTime; + result = prime * result + ((localNodeTime == null) ? 0 : localNodeTime.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Detection other = (Detection) obj; + if (count != other.count) { + return false; + } + if (duration != other.duration) { + return false; + } + String localGatewayTime = gatewayTime; + if (localGatewayTime == null) { + if (other.gatewayTime != null) { + return false; + } + } else if (!localGatewayTime.equals(other.gatewayTime)) { + return false; + } + String localNodeTime = nodeTime; + if (localNodeTime == null) { + if (other.nodeTime != null) { + return false; + } + } else if (!localNodeTime.equals(other.nodeTime)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Detection [count=" + count + ", gatewayTime=" + gatewayTime + ", nodeTime=" + nodeTime + + ", duration=" + duration + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartLockDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartLockDTO.java new file mode 100644 index 0000000000000..5f6ad3a73f991 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartLockDTO.java @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The SmartLock state of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureSmartLockDTO { + + private boolean autoRelockEnabled; + private @Nullable String deviceLabel; + private DoorLockVolumeSettings doorLockVolumeSettings = new DoorLockVolumeSettings(); + + public boolean getAutoRelockEnabled() { + return autoRelockEnabled; + } + + public @Nullable String getDeviceLabel() { + return deviceLabel; + } + + public DoorLockVolumeSettings getDoorLockVolumeSettings() { + return doorLockVolumeSettings; + } + + public static class DoorLockVolumeSettings { + private @Nullable String volume; + private @Nullable String voiceLevel; + private @Nullable String active; + private List availableVolumes = new ArrayList<>(); + private List availableVoiceLevels = new ArrayList<>(); + + public @Nullable String getVolume() { + return volume; + } + + public @Nullable String getVoiceLevel() { + return voiceLevel; + } + + public @Nullable String getActive() { + return active; + } + + public List getAvailableVolumes() { + return availableVolumes; + } + + public List getAvailableVoiceLevels() { + return availableVoiceLevels; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localActive = active; + result = prime * result + ((localActive == null) ? 0 : localActive.hashCode()); + result = prime * result + availableVoiceLevels.hashCode(); + result = prime * result + availableVolumes.hashCode(); + String localVoiceLevel = voiceLevel; + result = prime * result + ((localVoiceLevel == null) ? 0 : localVoiceLevel.hashCode()); + String localVolume = volume; + result = prime * result + ((localVolume == null) ? 0 : localVolume.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + DoorLockVolumeSettings other = (DoorLockVolumeSettings) obj; + String localActive = active; + if (localActive == null) { + if (other.active != null) { + return false; + } + } else if (!localActive.equals(other.active)) { + return false; + } + if (!availableVoiceLevels.equals(other.availableVoiceLevels)) { + return false; + } + if (!availableVolumes.equals(other.availableVolumes)) { + return false; + } + String localVoiceLevel = voiceLevel; + if (localVoiceLevel == null) { + if (other.voiceLevel != null) { + return false; + } + } else if (!localVoiceLevel.equals(other.voiceLevel)) { + return false; + } + String localVolume = volume; + if (localVolume == null) { + if (other.volume != null) { + return false; + } + } else if (!localVolume.equals(other.volume)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "DoorLockVolumeSettings [volume=" + volume + ", voiceLevel=" + voiceLevel + ", active=" + active + + ", availableVolumes=" + availableVolumes + ", availableVoiceLevels=" + availableVoiceLevels + "]"; + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (autoRelockEnabled ? 1231 : 1237); + String localDeviceLabel = deviceLabel; + result = prime * result + ((localDeviceLabel == null) ? 0 : localDeviceLabel.hashCode()); + result = prime * result + doorLockVolumeSettings.hashCode(); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + VerisureSmartLockDTO other = (VerisureSmartLockDTO) obj; + if (autoRelockEnabled != other.autoRelockEnabled) { + return false; + } + String localDeviceLabel = deviceLabel; + if (localDeviceLabel == null) { + if (other.deviceLabel != null) { + return false; + } + } else if (!localDeviceLabel.equals(other.deviceLabel)) { + return false; + } + if (!doorLockVolumeSettings.equals(other.doorLockVolumeSettings)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "VerisureSmartLockDTO [autoRelockEnabled=" + autoRelockEnabled + ", deviceLabel=" + deviceLabel + + ", doorLockVolumeSettings=" + doorLockVolumeSettings + "]"; + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartLocksDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartLocksDTO.java new file mode 100644 index 0000000000000..2613496698cf1 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartLocksDTO.java @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_SMARTLOCK; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The smart locks of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureSmartLocksDTO extends VerisureBaseThingDTO { + + private @Nullable VerisureSmartLockDTO smartLockJSON; + + public @Nullable VerisureSmartLockDTO getSmartLockJSON() { + return smartLockJSON; + } + + public void setSmartLockJSON(@Nullable VerisureSmartLockDTO smartLockJSON) { + this.smartLockJSON = smartLockJSON; + } + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_SMARTLOCK; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + VerisureSmartLockDTO localSmartLockJSON = smartLockJSON; + result = prime * result + ((localSmartLockJSON == null) ? 0 : localSmartLockJSON.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + VerisureSmartLocksDTO other = (VerisureSmartLocksDTO) obj; + VerisureSmartLockDTO localSmartLockJSON = smartLockJSON; + if (localSmartLockJSON == null) { + if (other.smartLockJSON != null) { + return false; + } + } else if (!localSmartLockJSON.equals(other.smartLockJSON)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "VerisureSmartLocksDTO [smartLockJSON=" + smartLockJSON + "]"; + } + + public static class Doorlock { + + @SerializedName("__typename") + private @Nullable String typename; + private @Nullable String currentLockState; + private @Nullable String eventTime; + private @Nullable String method; + private @Nullable String userString; + private Device device = new Device(); + private boolean motorJam; + private boolean secureModeActive; + + public @Nullable String getTypename() { + return typename; + } + + public @Nullable String getCurrentLockState() { + return currentLockState; + } + + public Device getDevice() { + return device; + } + + public @Nullable String getEventTime() { + return eventTime; + } + + public @Nullable String getMethod() { + return method; + } + + public boolean isMotorJam() { + return motorJam; + } + + public boolean getSecureModeActive() { + return secureModeActive; + } + + public @Nullable String getUserString() { + return userString; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localCurrentLockState = currentLockState; + result = prime * result + ((localCurrentLockState == null) ? 0 : localCurrentLockState.hashCode()); + result = prime * result + device.hashCode(); + String localEventTime = eventTime; + result = prime * result + ((localEventTime == null) ? 0 : localEventTime.hashCode()); + String localMethod = method; + result = prime * result + ((localMethod == null) ? 0 : localMethod.hashCode()); + result = prime * result + (motorJam ? 1231 : 1237); + result = prime * result + (secureModeActive ? 1231 : 1237); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + String localUserString = userString; + result = prime * result + ((localUserString == null) ? 0 : localUserString.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Doorlock other = (Doorlock) obj; + String localCurrentLockState = currentLockState; + if (localCurrentLockState == null) { + if (other.currentLockState != null) { + return false; + } + } else if (!localCurrentLockState.equals(other.currentLockState)) { + return false; + } + if (!device.equals(other.device)) { + return false; + } + String localEventTime = eventTime; + if (localEventTime == null) { + if (other.eventTime != null) { + return false; + } + } else if (!localEventTime.equals(other.eventTime)) { + return false; + } + String localMethod = method; + if (localMethod == null) { + if (other.method != null) { + return false; + } + } else if (!localMethod.equals(other.method)) { + return false; + } + if (motorJam != other.motorJam) { + return false; + } + if (secureModeActive != other.secureModeActive) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + String localUserString = userString; + if (localUserString == null) { + if (other.userString != null) { + return false; + } + } else if (!localUserString.equals(other.userString)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Doorlock [typename=" + typename + ", currentLockState=" + currentLockState + ", eventTime=" + + eventTime + ", method=" + method + ", userString=" + userString + ", device=" + device + + ", motorJam=" + motorJam + ", secureModeActive=" + secureModeActive + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartPlugsDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartPlugsDTO.java new file mode 100644 index 0000000000000..3f1b788ac1203 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureSmartPlugsDTO.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_SMARTPLUG; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The smart plugs of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureSmartPlugsDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_SMARTPLUG; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class Smartplug { + + private Device device = new Device(); + private @Nullable String currentState; + private @Nullable String icon; + private boolean isHazardous; + @SerializedName("__typename") + private @Nullable String typename; + + public Device getDevice() { + return device; + } + + public @Nullable String getCurrentState() { + return currentState; + } + + public @Nullable String getIcon() { + return icon; + } + + public boolean isHazardous() { + return isHazardous; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localCurrentState = currentState; + result = prime * result + ((localCurrentState == null) ? 0 : localCurrentState.hashCode()); + result = prime * result + device.hashCode(); + String localIcon = icon; + result = prime * result + ((localIcon == null) ? 0 : localIcon.hashCode()); + result = prime * result + (isHazardous ? 1231 : 1237); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Smartplug other = (Smartplug) obj; + String localCurrentState = currentState; + if (localCurrentState == null) { + if (other.currentState != null) { + return false; + } + } else if (!localCurrentState.equals(other.currentState)) { + return false; + } + if (!device.equals(other.device)) { + return false; + } + String localIcon = icon; + if (localIcon == null) { + if (other.icon != null) { + return false; + } + } else if (!localIcon.equals(other.icon)) { + return false; + } + if (isHazardous != other.isHazardous) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Smartplug [device=" + device + ", currentState=" + currentState + ", icon=" + icon + + ", isHazardous=" + isHazardous + ", typename=" + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureThingDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureThingDTO.java new file mode 100644 index 0000000000000..996611d3bc128 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureThingDTO.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The base identifer of all Verisure response objects. + * + * @author Jarle Hjortland - Initial contribution + * @author Jan Gustafsson - Further development + * + */ +@NonNullByDefault +public interface VerisureThingDTO { + + String getDeviceId(); + + void setDeviceId(String deviceId); + + @Nullable + String getLocation(); + + void setSiteName(@Nullable String siteName); + + @Nullable + String getSiteName(); + + void setSiteId(BigDecimal siteId); + + BigDecimal getSiteId(); + + ThingTypeUID getThingTypeUID(); +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureUserPresencesDTO.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureUserPresencesDTO.java new file mode 100644 index 0000000000000..dba37994f99eb --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/dto/VerisureUserPresencesDTO.java @@ -0,0 +1,247 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.dto; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.THING_TYPE_USERPRESENCE; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The user presences of the Verisure System. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureUserPresencesDTO extends VerisureBaseThingDTO { + + @Override + public ThingTypeUID getThingTypeUID() { + return THING_TYPE_USERPRESENCE; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return true; + } + + public static class UserTracking { + private boolean isCallingUser; + private @Nullable String webAccount; + private @Nullable String status; + private @Nullable String xbnContactId; + private @Nullable String currentLocationName; + private String deviceId = ""; + private @Nullable String name; + private @Nullable String currentLocationTimestamp; + private @Nullable String deviceName; + private @Nullable String currentLocationId; + @SerializedName("__typename") + private @Nullable String typename; + + public boolean getIsCallingUser() { + return isCallingUser; + } + + public @Nullable String getWebAccount() { + return webAccount; + } + + public @Nullable String getStatus() { + return status; + } + + public @Nullable String getXbnContactId() { + return xbnContactId; + } + + public @Nullable String getCurrentLocationName() { + return currentLocationName; + } + + public @Nullable String getDeviceId() { + return deviceId; + } + + public @Nullable String getName() { + return name; + } + + public @Nullable String getCurrentLocationTimestamp() { + return currentLocationTimestamp; + } + + public @Nullable String getDeviceName() { + return deviceName; + } + + public @Nullable String getCurrentLocationId() { + return currentLocationId; + } + + public @Nullable String getTypename() { + return typename; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + String localCurrentLocationId = currentLocationId; + result = prime * result + ((localCurrentLocationId == null) ? 0 : localCurrentLocationId.hashCode()); + String localCurrentLocationName = currentLocationName; + result = prime * result + ((localCurrentLocationName == null) ? 0 : localCurrentLocationName.hashCode()); + String localCurrentLocationTimestamp = currentLocationTimestamp; + result = prime * result + + ((localCurrentLocationTimestamp == null) ? 0 : localCurrentLocationTimestamp.hashCode()); + result = prime * result + deviceId.hashCode(); + String localDeviceName = deviceName; + result = prime * result + ((localDeviceName == null) ? 0 : localDeviceName.hashCode()); + result = prime * result + (isCallingUser ? 1231 : 1237); + String localName = name; + result = prime * result + ((localName == null) ? 0 : localName.hashCode()); + String localStatus = status; + result = prime * result + ((localStatus == null) ? 0 : localStatus.hashCode()); + String localTypeName = typename; + result = prime * result + ((localTypeName == null) ? 0 : localTypeName.hashCode()); + String localWebAccount = webAccount; + result = prime * result + ((localWebAccount == null) ? 0 : localWebAccount.hashCode()); + String localXbnContactId = xbnContactId; + result = prime * result + ((localXbnContactId == null) ? 0 : localXbnContactId.hashCode()); + return result; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + UserTracking other = (UserTracking) obj; + String localCurrentLocationId = currentLocationId; + if (localCurrentLocationId == null) { + if (other.currentLocationId != null) { + return false; + } + } else if (!localCurrentLocationId.equals(other.currentLocationId)) { + return false; + } + String localCurrentLocationName = currentLocationName; + if (localCurrentLocationName == null) { + if (other.currentLocationName != null) { + return false; + } + } else if (!localCurrentLocationName.equals(other.currentLocationName)) { + return false; + } + String localCurrentLocationTimestamp = currentLocationTimestamp; + if (localCurrentLocationTimestamp == null) { + if (other.currentLocationTimestamp != null) { + return false; + } + } else if (!localCurrentLocationTimestamp.equals(other.currentLocationTimestamp)) { + return false; + } + if (!deviceId.equals(other.deviceId)) { + return false; + } + String localDeviceName = deviceName; + if (localDeviceName == null) { + if (other.deviceName != null) { + return false; + } + } else if (!localDeviceName.equals(other.deviceName)) { + return false; + } + if (isCallingUser != other.isCallingUser) { + return false; + } + String localName = name; + if (localName == null) { + if (other.name != null) { + return false; + } + } else if (!localName.equals(other.name)) { + return false; + } + String localStatus = status; + if (localStatus == null) { + if (other.status != null) { + return false; + } + } else if (!localStatus.equals(other.status)) { + return false; + } + String localTypeName = typename; + if (localTypeName == null) { + if (other.typename != null) { + return false; + } + } else if (!localTypeName.equals(other.typename)) { + return false; + } + String localWebAccount = webAccount; + if (localWebAccount == null) { + if (other.webAccount != null) { + return false; + } + } else if (!localWebAccount.equals(other.webAccount)) { + return false; + } + String localXbnContactId = xbnContactId; + if (localXbnContactId == null) { + if (other.xbnContactId != null) { + return false; + } + } else if (!localXbnContactId.equals(other.xbnContactId)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "UserTracking [isCallingUser=" + isCallingUser + ", webAccount=" + webAccount + ", status=" + status + + ", xbnContactId=" + xbnContactId + ", currentLocationName=" + currentLocationName + ", deviceId=" + + deviceId + ", name=" + name + ", currentLocationTimestamp=" + currentLocationTimestamp + + ", deviceName=" + deviceName + ", currentLocationId=" + currentLocationId + ", typename=" + + typename + "]"; + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureAlarmThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureAlarmThingHandler.java new file mode 100644 index 0000000000000..ca245a32672f8 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureAlarmThingHandler.java @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.verisure.internal.VerisureSession; +import org.openhab.binding.verisure.internal.dto.VerisureAlarmsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureAlarmsDTO.ArmState; + +/** + * Handler for the Alarm Device thing type that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureAlarmThingHandler extends VerisureThingHandler { + + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_ALARM); + + private static final int REFRESH_DELAY_SECONDS = 10; + + public VerisureAlarmThingHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand, channel: {}, command: {}", channelUID, command); + if (command instanceof RefreshType) { + super.handleCommand(channelUID, command); + } else if (channelUID.getId().equals(CHANNEL_ALARM_STATUS)) { + handleAlarmState(command); + scheduleImmediateRefresh(REFRESH_DELAY_SECONDS); + } else { + logger.warn("Unknown command! {}", command); + } + } + + private void handleAlarmState(Command command) { + String deviceId = config.getDeviceId(); + VerisureSession session = getSession(); + if (session != null) { + VerisureAlarmsDTO alarm = session.getVerisureThing(deviceId, getVerisureThingClass()); + if (alarm != null) { + BigDecimal installationId = alarm.getSiteId(); + String pinCode = session.getPinCode(installationId); + + if (pinCode != null) { + String url = START_GRAPHQL; + String operation, state = ""; + + switch (command.toString()) { + case "DISARMED": + operation = "disarm"; + state = "armStateDisarm"; + break; + case "ARMED_HOME": + operation = "armHome"; + state = "armStateArmHome"; + break; + case "ARMED_AWAY": + operation = "armAway"; + state = "armStateArmAway"; + break; + default: + logger.warn("Unknown alarm command: {}", command); + return; + } + + ArrayList list = new ArrayList<>(); + AlarmDTO alarmJSON = new AlarmDTO(); + VariablesDTO variables = new VariablesDTO(); + + variables.setCode(pinCode); + variables.setGiid(installationId.toString()); + alarmJSON.setVariables(variables); + alarmJSON.setOperationName(operation); + String query = "mutation " + operation + "($giid: String!, $code: String!) {\n " + state + + "(giid: $giid, code: $code)\n}\n"; + alarmJSON.setQuery(query); + list.add(alarmJSON); + + String queryQLAlarmSetState = gson.toJson(list); + logger.debug("Trying to set alarm state to {} with URL {} and data {}", operation, url, + queryQLAlarmSetState); + + int httpResultCode = session.sendCommand(url, queryQLAlarmSetState, installationId); + if (httpResultCode == HttpStatus.OK_200) { + logger.debug("Alarm status successfully changed!"); + } else { + logger.warn("Could not send command, HTTP result code: {}", httpResultCode); + } + } else { + logger.warn("PIN code is not configured! Mandatory to control Alarm!"); + } + } + } + } + + @Override + public Class getVerisureThingClass() { + return VerisureAlarmsDTO.class; + } + + @Override + public synchronized void update(VerisureAlarmsDTO thing) { + updateAlarmState(thing); + updateStatus(ThingStatus.ONLINE); + } + + private void updateAlarmState(VerisureAlarmsDTO alarmsJSON) { + ArmState armState = alarmsJSON.getData().getInstallation().getArmState(); + String alarmStatus = armState.getStatusType(); + if (alarmStatus != null) { + getThing().getChannels().stream().map(Channel::getUID) + .filter(channelUID -> isLinked(channelUID) && !channelUID.getId().equals("timestamp")) + .forEach(channelUID -> { + State state = getValue(channelUID.getId(), armState); + updateState(channelUID, state); + }); + updateTimeStamp(armState.getDate()); + updateInstallationChannels(alarmsJSON); + } else { + logger.warn("Alarm status is null!"); + } + } + + public State getValue(String channelId, ArmState armState) { + switch (channelId) { + case CHANNEL_ALARM_STATUS: + return new StringType(armState.getStatusType()); + case CHANNEL_CHANGED_BY_USER: + return new StringType(armState.getName()); + case CHANNEL_CHANGED_VIA: + return new StringType(armState.getChangedVia()); + } + return UnDefType.UNDEF; + } + + private static class AlarmDTO { + + @SuppressWarnings("unused") + private @Nullable String operationName; + @SuppressWarnings("unused") + private VariablesDTO variables = new VariablesDTO(); + @SuppressWarnings("unused") + private @Nullable String query; + + public void setOperationName(String operationName) { + this.operationName = operationName; + } + + public void setVariables(VariablesDTO variables) { + this.variables = variables; + } + + public void setQuery(String query) { + this.query = query; + } + } + + private static class VariablesDTO { + + @SuppressWarnings("unused") + private @Nullable String giid; + @SuppressWarnings("unused") + private @Nullable String code; + + public void setGiid(String giid) { + this.giid = giid; + } + + public void setCode(String code) { + this.code = code; + } + } + + @Override + public void updateTriggerChannel(String event) { + logger.debug("ThingHandler trigger event {}", event); + triggerChannel(CHANNEL_SMARTLOCK_TRIGGER_CHANNEL, event); + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java new file mode 100644 index 0000000000000..cc901ae15cf2a --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBridgeHandler.java @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.openhab.binding.verisure.internal.DeviceStatusListener; +import org.openhab.binding.verisure.internal.VerisureBridgeConfiguration; +import org.openhab.binding.verisure.internal.VerisureSession; +import org.openhab.binding.verisure.internal.discovery.VerisureThingDiscoveryService; +import org.openhab.binding.verisure.internal.dto.VerisureThingDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VerisureBridgeHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author l3rum - Initial contribution + * @author Jan Gustafsson - Furher development + */ +@NonNullByDefault +public class VerisureBridgeHandler extends BaseBridgeHandler { + + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE); + + private static final int REFRESH_DELAY_SECONDS = 30; + private final Logger logger = LoggerFactory.getLogger(VerisureBridgeHandler.class); + private final ReentrantLock immediateRefreshJobLock = new ReentrantLock(); + private final HttpClient httpClient; + + private String authstring = ""; + private @Nullable String pinCode; + private static int REFRESH_SEC = 600; + private @Nullable ScheduledFuture refreshJob; + private @Nullable ScheduledFuture immediateRefreshJob; + private @Nullable VerisureSession session; + + public VerisureBridgeHandler(Bridge bridge, HttpClient httpClient) { + super(bridge); + this.httpClient = httpClient; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("VerisureBridgeHandler Handle command {} on channelUID: {}", command, channelUID); + if (command instanceof RefreshType) { + if (channelUID.getId().equals(CHANNEL_STATUS) && channelUID.getThingUID().equals(getThing().getUID())) { + logger.debug("Refresh command on status channel {} will trigger instant refresh", channelUID); + scheduleImmediateRefresh(0); + } else { + logger.debug("Refresh command on channel {} will trigger refresh in {} seconds", channelUID, + REFRESH_DELAY_SECONDS); + scheduleImmediateRefresh(REFRESH_DELAY_SECONDS); + } + } else { + logger.warn("unknown command! {}", command); + } + } + + public @Nullable VerisureSession getSession() { + return session; + } + + public @Nullable ThingUID getUID() { + return getThing().getUID(); + } + + @Override + public void initialize() { + logger.debug("Initializing Verisure Binding"); + VerisureBridgeConfiguration config = getConfigAs(VerisureBridgeConfiguration.class); + REFRESH_SEC = config.refresh; + this.pinCode = config.pin; + if (config.username == null || config.password == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Configuration of username and password is mandatory"); + } else if (REFRESH_SEC < 0) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Refresh time cannot negative!"); + } else { + try { + authstring = "j_username=" + config.username + "&j_password=" + + URLEncoder.encode(config.password, StandardCharsets.UTF_8.toString()) + + "&spring-security-redirect=" + START_REDIRECT; + scheduler.execute(() -> { + + if (session == null) { + logger.debug("Session is null, let's create a new one"); + session = new VerisureSession(this.httpClient); + } + VerisureSession session = this.session; + updateStatus(ThingStatus.UNKNOWN); + if (session != null) { + if (!session.initialize(authstring, pinCode, config.username)) { + logger.warn("Failed to initialize bridge, please check your credentials!"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_REGISTERING_ERROR, + "Failed to login to Verisure, please check your credentials!"); + return; + } + startAutomaticRefresh(); + } + }); + } catch (RuntimeException | UnsupportedEncodingException e) { + logger.warn("Failed to initialize: {}", e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + } + + @Override + public void dispose() { + logger.debug("Handler disposed."); + stopAutomaticRefresh(); + stopImmediateRefresh(); + session = null; + } + + public boolean registerObjectStatusListener( + DeviceStatusListener deviceStatusListener) { + VerisureSession mySession = session; + if (mySession != null) { + logger.debug("registerObjectStatusListener for listener {}", deviceStatusListener); + return mySession.registerDeviceStatusListener(deviceStatusListener); + } + return false; + } + + public boolean unregisterObjectStatusListener( + DeviceStatusListener deviceStatusListener) { + VerisureSession mySession = session; + if (mySession != null) { + logger.debug("unregisterObjectStatusListener for listener {}", deviceStatusListener); + return mySession.unregisterDeviceStatusListener(deviceStatusListener); + } + return false; + } + + @Override + public Collection> getServices() { + return Collections.singleton(VerisureThingDiscoveryService.class); + } + + private void refreshAndUpdateStatus() { + logger.debug("Refresh and update status!"); + VerisureSession session = this.session; + if (session != null) { + boolean success = session.refresh(); + if (success) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + } + } + } + + void scheduleImmediateRefresh(int refreshDelay) { + logger.debug("VerisureBridgeHandler - scheduleImmediateRefresh"); + immediateRefreshJobLock.lock(); + ScheduledFuture refreshJob = this.refreshJob; + ScheduledFuture immediateRefreshJob = this.immediateRefreshJob; + try { + // We schedule in 10 sec, to avoid multiple updates + if (refreshJob != null) { + logger.debug("Current remaining delay {} for refresh job {}", refreshJob.getDelay(TimeUnit.SECONDS), + refreshJob); + if (immediateRefreshJob != null) { + logger.debug("Current remaining delay {} for immediate refresh job {}", + immediateRefreshJob.getDelay(TimeUnit.SECONDS), immediateRefreshJob); + } + + if (refreshJob.getDelay(TimeUnit.SECONDS) > refreshDelay) { + if (immediateRefreshJob == null || immediateRefreshJob.getDelay(TimeUnit.SECONDS) <= 0) { + if (immediateRefreshJob != null) { + logger.debug("Current remaining delay {} for immediate refresh job {}", + immediateRefreshJob.getDelay(TimeUnit.SECONDS), immediateRefreshJob); + } + // Note we are using getDelay() instead of isDone() as we want to allow Things to schedule a + // refresh if their status is pending. As the status update happens inside the + // refreshAndUpdateStatus + // execution the isDone() will return false and would not allow the rescheduling of the task. + this.immediateRefreshJob = scheduler.schedule(this::refreshAndUpdateStatus, refreshDelay, + TimeUnit.SECONDS); + logger.debug("Scheduling new immediate refresh job {}", immediateRefreshJob); + } + } + } + } catch (RejectedExecutionException e) { + logger.warn("Immediate refresh job cannot be scheduled!"); + } finally { + immediateRefreshJobLock.unlock(); + } + } + + private void startAutomaticRefresh() { + ScheduledFuture refreshJob = this.refreshJob; + logger.debug("Start automatic refresh {}", refreshJob); + if (refreshJob == null || refreshJob.isCancelled()) { + try { + this.refreshJob = scheduler.scheduleWithFixedDelay(this::refreshAndUpdateStatus, 0, REFRESH_SEC, + TimeUnit.SECONDS); + logger.debug("Scheduling at fixed delay refreshjob {}", this.refreshJob); + } catch (RejectedExecutionException e) { + logger.warn("Automatic refresh job cannot be started!"); + } + } + } + + private void stopAutomaticRefresh() { + ScheduledFuture refreshJob = this.refreshJob; + logger.debug("Stop automatic refresh for job {}", refreshJob); + if (refreshJob != null) { + refreshJob.cancel(true); + this.refreshJob = null; + } + } + + private void stopImmediateRefresh() { + immediateRefreshJobLock.lock(); + ScheduledFuture immediateRefreshJob = this.immediateRefreshJob; + try { + logger.debug("Stop immediate refresh for job {}", immediateRefreshJob); + if (immediateRefreshJob != null) { + immediateRefreshJob.cancel(true); + this.immediateRefreshJob = null; + } + } catch (RejectedExecutionException e) { + logger.warn("Immediate refresh job cannot be scheduled!"); + } finally { + immediateRefreshJobLock.unlock(); + } + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBroadbandConnectionThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBroadbandConnectionThingHandler.java new file mode 100644 index 0000000000000..ed001e9a4531e --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureBroadbandConnectionThingHandler.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.openhab.binding.verisure.internal.dto.VerisureBroadbandConnectionsDTO; + +/** + * Handler for the Broadband COnnection thing type that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureBroadbandConnectionThingHandler extends VerisureThingHandler { + + public static final Set SUPPORTED_THING_TYPES = Collections + .singleton(THING_TYPE_BROADBAND_CONNECTION); + + public VerisureBroadbandConnectionThingHandler(Thing thing) { + super(thing); + } + + @Override + public Class getVerisureThingClass() { + return VerisureBroadbandConnectionsDTO.class; + } + + @Override + public synchronized void update(VerisureBroadbandConnectionsDTO thing) { + updateBroadbandConnection(thing); + updateStatus(ThingStatus.ONLINE); + } + + private void updateBroadbandConnection(VerisureBroadbandConnectionsDTO vbcJSON) { + String testDate = vbcJSON.getData().getInstallation().getBroadband().getTestDate(); + if (testDate != null) { + updateTimeStamp(testDate); + ChannelUID cuid = new ChannelUID(getThing().getUID(), CHANNEL_CONNECTED); + boolean broadbandConnected = vbcJSON.getData().getInstallation().getBroadband().isBroadbandConnected(); + updateState(cuid, OnOffType.from(broadbandConnected)); + updateInstallationChannels(vbcJSON); + } + } + + @Override + public void updateTriggerChannel(String event) { + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java new file mode 100644 index 0000000000000..4826fd523a971 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureClimateDeviceThingHandler.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.util.HashSet; +import java.util.Set; + +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.unit.SIUnits; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.verisure.internal.dto.VerisureClimatesDTO; + +/** + * Handler for all Climate Device thing types that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureClimateDeviceThingHandler extends VerisureThingHandler { + + public static final Set SUPPORTED_THING_TYPES = new HashSet(); + static { + SUPPORTED_THING_TYPES.add(THING_TYPE_SMOKEDETECTOR); + SUPPORTED_THING_TYPES.add(THING_TYPE_WATERDETECTOR); + SUPPORTED_THING_TYPES.add(THING_TYPE_SIREN); + SUPPORTED_THING_TYPES.add(THING_TYPE_NIGHT_CONTROL); + } + + public VerisureClimateDeviceThingHandler(Thing thing) { + super(thing); + } + + @Override + public Class getVerisureThingClass() { + return VerisureClimatesDTO.class; + } + + @Override + public synchronized void update(VerisureClimatesDTO thing) { + updateClimateDeviceState(thing); + updateStatus(ThingStatus.ONLINE); + } + + private void updateClimateDeviceState(VerisureClimatesDTO climateJSON) { + getThing().getChannels().stream().map(Channel::getUID) + .filter(channelUID -> isLinked(channelUID) && !channelUID.getId().equals("timestamp")) + .forEach(channelUID -> { + State state = getValue(channelUID.getId(), climateJSON); + updateState(channelUID, state); + }); + String timeStamp = climateJSON.getData().getInstallation().getClimates().get(0).getTemperatureTimestamp(); + if (timeStamp != null) { + updateTimeStamp(timeStamp); + } + updateInstallationChannels(climateJSON); + } + + public State getValue(String channelId, VerisureClimatesDTO climateJSON) { + switch (channelId) { + case CHANNEL_TEMPERATURE: + double temperature = climateJSON.getData().getInstallation().getClimates().get(0).getTemperatureValue(); + return new QuantityType(temperature, SIUnits.CELSIUS); + case CHANNEL_HUMIDITY: + if (climateJSON.getData().getInstallation().getClimates().get(0).isHumidityEnabled()) { + double humidity = climateJSON.getData().getInstallation().getClimates().get(0).getHumidityValue(); + return new QuantityType(humidity, SmartHomeUnits.PERCENT); + } + case CHANNEL_HUMIDITY_ENABLED: + boolean humidityEnabled = climateJSON.getData().getInstallation().getClimates().get(0) + .isHumidityEnabled(); + return OnOffType.from(humidityEnabled); + case CHANNEL_LOCATION: + String location = climateJSON.getLocation(); + return location != null ? new StringType(location) : UnDefType.NULL; + } + return UnDefType.UNDEF; + } + + @Override + public void updateTriggerChannel(String event) { + logger.debug("ClimateThingHandler trigger event {}", event); + triggerChannel(CHANNEL_SMOKE_DETECTION_TRIGGER_CHANNEL, event); + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java new file mode 100644 index 0000000000000..26a56be7c8265 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureDoorWindowThingHandler.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.verisure.internal.dto.VerisureDoorWindowsDTO; +import org.openhab.binding.verisure.internal.dto.VerisureDoorWindowsDTO.DoorWindow; + +/** + * Handler for the Smart Lock Device thing type that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureDoorWindowThingHandler extends VerisureThingHandler { + + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DOORWINDOW); + + public VerisureDoorWindowThingHandler(Thing thing) { + super(thing); + } + + @Override + public Class getVerisureThingClass() { + return VerisureDoorWindowsDTO.class; + } + + @Override + public synchronized void update(VerisureDoorWindowsDTO thing) { + updateDoorWindowState(thing); + updateStatus(ThingStatus.ONLINE); + } + + private void updateDoorWindowState(VerisureDoorWindowsDTO doorWindowJSON) { + List doorWindowList = doorWindowJSON.getData().getInstallation().getDoorWindows(); + if (!doorWindowList.isEmpty()) { + DoorWindow doorWindow = doorWindowList.get(0); + + getThing().getChannels().stream().map(Channel::getUID) + .filter(channelUID -> isLinked(channelUID) && !channelUID.getId().equals("timestamp")) + .forEach(channelUID -> { + State state = getValue(channelUID.getId(), doorWindow); + updateState(channelUID, state); + + }); + updateTimeStamp(doorWindow.getReportTime()); + updateInstallationChannels(doorWindowJSON); + } else { + logger.debug("DoorWindow list is empty!"); + } + } + + public State getValue(String channelId, DoorWindow doorWindow) { + switch (channelId) { + case CHANNEL_STATE: + return "OPEN".equals(doorWindow.getState()) ? OpenClosedType.OPEN : OpenClosedType.CLOSED; + case CHANNEL_LOCATION: + String location = doorWindow.getDevice().getArea(); + return location != null ? new StringType(location) : UnDefType.UNDEF; + } + return UnDefType.UNDEF; + } + + @Override + public void updateTriggerChannel(String event) { + logger.debug("DoorWindowThingHandler trigger event {}", event); + triggerChannel(CHANNEL_DOOR_WINDOW_TRIGGER_CHANNEL, event); + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureEventLogThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureEventLogThingHandler.java new file mode 100644 index 0000000000000..469be44b77836 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureEventLogThingHandler.java @@ -0,0 +1,174 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.verisure.internal.VerisureSession; +import org.openhab.binding.verisure.internal.VerisureThingConfiguration; +import org.openhab.binding.verisure.internal.dto.VerisureBaseThingDTO.Device; +import org.openhab.binding.verisure.internal.dto.VerisureEventLogDTO; +import org.openhab.binding.verisure.internal.dto.VerisureEventLogDTO.EventLog; +import org.openhab.binding.verisure.internal.dto.VerisureEventLogDTO.PagedList; + +/** + * Handler for the Event Log thing type that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureEventLogThingHandler extends VerisureThingHandler { + + private BigDecimal lastEventId = BigDecimal.ZERO; + private long lastEventTime = 0; + + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_EVENT_LOG); + + public VerisureEventLogThingHandler(Thing thing) { + super(thing); + } + + @Override + public Class getVerisureThingClass() { + return VerisureEventLogDTO.class; + } + + @Override + public synchronized void update(VerisureEventLogDTO thing) { + updateEventLogState(thing); + updateStatus(ThingStatus.ONLINE); + } + + @Override + public void initialize() { + logger.debug("initialize on thing: {}", thing); + VerisureSession session = getSession(); + config = getConfigAs(VerisureThingConfiguration.class); + if (session != null) { + logger.debug("Set number of events to fetch from API to {} for thing {}", config.getNumberOfEvents(), + thing); + session.setNumberOfEvents(config.getNumberOfEvents()); + } + super.initialize(); + } + + private void updateEventLogState(VerisureEventLogDTO eventLogJSON) { + EventLog eventLog = eventLogJSON.getData().getInstallation().getEventLog(); + if (eventLog.getPagedList().size() > 0) { + getThing().getChannels().stream().map(Channel::getUID).filter(channelUID -> isLinked(channelUID)) + .forEach(channelUID -> { + State state = getValue(channelUID.getId(), eventLogJSON, eventLog); + updateState(channelUID, state); + }); + updateInstallationChannels(eventLogJSON); + String eventTime = eventLogJSON.getData().getInstallation().getEventLog().getPagedList().get(0) + .getEventTime(); + if (eventTime != null) { + updateTimeStamp(eventTime, CHANNEL_LAST_EVENT_TIME); + lastEventTime = ZonedDateTime.parse(eventTime).toEpochSecond(); + } + } else { + logger.debug("Empty event log."); + } + } + + public State getValue(String channelId, VerisureEventLogDTO verisureEventLog, EventLog eventLog) { + Device device = eventLog.getPagedList().get(0).getDevice(); + + switch (channelId) { + case CHANNEL_LAST_EVENT_LOCATION: + return device != null && device.getArea() != null ? new StringType(device.getArea()) : UnDefType.NULL; + case CHANNEL_LAST_EVENT_DEVICE_ID: + return device != null && device.getDeviceLabel() != null ? new StringType(device.getDeviceLabel()) + : UnDefType.NULL; + case CHANNEL_LAST_EVENT_ID: + String eventId = eventLog.getPagedList().get(0).getEventId(); + if (eventId != null) { + if (eventId.contains("-")) { + eventId = eventId.replace("-", ""); + lastEventId = new BigDecimal(new BigInteger(eventId, 16)); + } else { + lastEventId = new BigDecimal(eventId); + } + return new DecimalType(lastEventId); + } else { + return UnDefType.NULL; + } + case CHANNEL_LAST_EVENT_TIME: + if (lastEventTime != 0) { + triggerEventChannels(eventLog); + } + case CHANNEL_LAST_EVENT_DEVICE_TYPE: + return device != null && device.getGui().getLabel() != null ? new StringType(device.getGui().getLabel()) + : UnDefType.NULL; + case CHANNEL_LAST_EVENT_TYPE: + String lastEventType = eventLog.getPagedList().get(0).getEventType(); + return lastEventType != null ? new StringType(lastEventType) : UnDefType.NULL; + case CHANNEL_LAST_EVENT_CATEGORY: + String lastEventCategory = eventLog.getPagedList().get(0).getEventCategory(); + return lastEventCategory != null ? new StringType(lastEventCategory) : UnDefType.NULL; + case CHANNEL_LAST_EVENT_USER_NAME: + String lastEventUserName = eventLog.getPagedList().get(0).getUserName(); + return lastEventUserName != null ? new StringType(lastEventUserName) : UnDefType.NULL; + case CHANNEL_EVENT_LOG: + String eventLogJSON = gson.toJson(eventLog); + return eventLogJSON != null ? new StringType(eventLogJSON) : UnDefType.NULL; + } + return UnDefType.UNDEF; + } + + private void triggerEventChannels(EventLog eventLog) { + List newEventList = eventLog.getPagedList().stream().collect(Collectors.toList()); + Collections.reverse(newEventList); + ArrayList events = new ArrayList<>(); + for (PagedList newEvent : newEventList) { + long eventTime = ZonedDateTime.parse(newEvent.getEventTime()).toEpochSecond(); + logger.trace("Event time: {} Last Event time: {}", eventTime, lastEventTime); + if (eventTime > lastEventTime) { + logger.debug("Create event {} for event time {}", newEvent.getEventType(), eventTime); + Event event; + Device device = newEvent.getDevice(); + if (device != null) { + event = new Event(device.getDeviceLabel(), newEvent.getEventType(), newEvent.getEventCategory()); + } else { + event = new Event("NA", newEvent.getEventType(), newEvent.getEventCategory()); + } + events.add(event); + } + } + updateTriggerChannel(events); + } + + @Override + public void updateTriggerChannel(String event) { + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureGatewayThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureGatewayThingHandler.java new file mode 100644 index 0000000000000..d8abe38ed36b9 --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureGatewayThingHandler.java @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.verisure.internal.dto.VerisureGatewayDTO; +import org.openhab.binding.verisure.internal.dto.VerisureGatewayDTO.CommunicationState; + +/** + * Handler for the Gateway thing type that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureGatewayThingHandler extends VerisureThingHandler { + + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_GATEWAY); + + public VerisureGatewayThingHandler(Thing thing) { + super(thing); + } + + @Override + public Class getVerisureThingClass() { + return VerisureGatewayDTO.class; + } + + @Override + public synchronized void update(VerisureGatewayDTO thing) { + updateGatewayState(thing); + updateStatus(ThingStatus.ONLINE); + } + + private void updateGatewayState(VerisureGatewayDTO gatewayJSON) { + List communicationStateList = gatewayJSON.getData().getInstallation() + .getCommunicationState(); + if (!communicationStateList.isEmpty()) { + communicationStateList.forEach(communicationState -> { + getThing().getChannels().stream().map(Channel::getUID).filter(channelUID -> isLinked(channelUID)) + .forEach(channelUID -> { + if (!channelUID.getId().contains("testTime")) { + State state = getValue(channelUID.getId(), gatewayJSON, communicationState); + updateState(channelUID, state); + } else { + String timestamp = communicationState.getTestDate(); + if (timestamp != null && channelUID.toString() + .contains(communicationState.getHardwareCarrierType())) { + updateTimeStamp(timestamp, channelUID); + } + } + }); + }); + updateInstallationChannels(gatewayJSON); + } else { + logger.debug("Empty communication state list."); + } + } + + public State getValue(String channelId, VerisureGatewayDTO verisureGateway, CommunicationState communicationState) { + switch (channelId) { + case CHANNEL_STATUS_GSM_OVER_UDP: + case CHANNEL_STATUS_GSM_OVER_SMS: + case CHANNEL_STATUS_GPRS_OVER_UDP: + case CHANNEL_STATUS_ETH_OVER_UDP: + String state = communicationState.getResult(); + return state != null ? new StringType(state) : UnDefType.NULL; + case CHANNEL_GATEWAY_MODEL: + String model = communicationState.getDevice().getGui().getLabel(); + return model != null ? new StringType(model) : UnDefType.NULL; + case CHANNEL_LOCATION: + String location = communicationState.getDevice().getArea(); + return location != null ? new StringType(location) : UnDefType.NULL; + } + return UnDefType.UNDEF; + } + + @Override + public void updateTriggerChannel(String event) { + logger.debug("GatewayThingHandler trigger event {}", event); + triggerChannel(CHANNEL_GATEWAY_TRIGGER_CHANNEL, event); + } +} diff --git a/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureMiceDetectionThingHandler.java b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureMiceDetectionThingHandler.java new file mode 100644 index 0000000000000..31067bce1092c --- /dev/null +++ b/bundles/org.openhab.binding.verisure/src/main/java/org/openhab/binding/verisure/internal/handler/VerisureMiceDetectionThingHandler.java @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.verisure.internal.handler; + +import static org.openhab.binding.verisure.internal.VerisureBindingConstants.*; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.measure.quantity.Temperature; +import javax.measure.quantity.Time; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.unit.SIUnits; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.verisure.internal.dto.VerisureMiceDetectionDTO; +import org.openhab.binding.verisure.internal.dto.VerisureMiceDetectionDTO.Detection; +import org.openhab.binding.verisure.internal.dto.VerisureMiceDetectionDTO.Mouse; + +/** + * Handler for the Mice Detection thing type that Verisure provides. + * + * @author Jan Gustafsson - Initial contribution + * + */ +@NonNullByDefault +public class VerisureMiceDetectionThingHandler extends VerisureThingHandler { + + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_MICE_DETECTION); + + public VerisureMiceDetectionThingHandler(Thing thing) { + super(thing); + } + + @Override + public Class getVerisureThingClass() { + return VerisureMiceDetectionDTO.class; + } + + @Override + public synchronized void update(VerisureMiceDetectionDTO thing) { + updateMiceDetectionState(thing); + updateStatus(ThingStatus.ONLINE); + } + + private void updateMiceDetectionState(VerisureMiceDetectionDTO miceDetectionJSON) { + List miceList = miceDetectionJSON.getData().getInstallation().getMice(); + if (!miceList.isEmpty()) { + Mouse mouse = miceList.get(0); + getThing().getChannels().stream().map(Channel::getUID).filter(channelUID -> isLinked(channelUID) + && !channelUID.getId().equals("timestamp") && !channelUID.getId().equals("temperatureTimestamp")) + .forEach(channelUID -> { + State state = getValue(channelUID.getId(), miceDetectionJSON, mouse); + updateState(channelUID, state); + }); + if (mouse.getDetections().size() != 0) { + updateTimeStamp(mouse.getDetections().get(0).getNodeTime()); + } + updateTimeStamp(miceDetectionJSON.getTemperatureTime(), CHANNEL_TEMPERATURE_TIMESTAMP); + updateInstallationChannels(miceDetectionJSON); + } else { + logger.debug("MiceList is empty!"); + } + } + + public State getValue(String channelId, VerisureMiceDetectionDTO miceDetectionJSON, Mouse mouse) { + switch (channelId) { + case CHANNEL_COUNT_LATEST_DETECTION: + if (mouse.getDetections().size() == 0) { + return new DecimalType(0); + } else { + return new DecimalType(mouse.getDetections().get(0).getCount()); + } + case CHANNEL_COUNT_LAST_24_HOURS: + if (mouse.getDetections().size() == 0) { + return new DecimalType(0); + } else { + return new DecimalType(mouse.getDetections().stream().mapToInt(Detection::getCount).sum()); + } + case CHANNEL_DURATION_LATEST_DETECTION: + if (mouse.getDetections().size() == 0) { + return new QuantityType