[hue] Support new home security products (#15601)

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green 2023-11-09 07:09:20 +00:00 committed by GitHub
parent 5555d68f92
commit 4f6d33bb79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 473 additions and 29 deletions

View File

@ -59,35 +59,40 @@ The configuration of all things (as described above) is the same regardless of w
Device things support some of the following channels:
| Channel ID | Item Type | Description |
|---------------------------|--------------------|---------------------------------------------------------------------------------------------------------------------|
| color | Color | Supports full color control with hue, saturation and brightness values, or brightness only, or switching on or off. |
| brightness | Dimmer | Supports control of the brightness value, or switching on or off. |
| color-temperature | Dimmer | Supports control of the color temperature in percent from cold (0%) to warm (100%). |
| color-temperature-abs | Number:Temperature | Supports control of the color temperature via a QuantityType having a temperature unit e.g. Kelvin. (Advanced) |
| switch | Switch | Supports switching the device on and off. |
| dynamics | Number:Time | Sets the duration of dynamic transitions between light states. (Advanced) |
| alert | String | Allows setting an alert on a light e.g. flashing them. (Advanced) |
| effect | String | Allows setting an effect on a light e.g. 'candle' effect. (Advanced) |
| button-last-event | (String) | Informs which button was last pressed in the device. (Trigger Channel) |
| button-last-updated | DateTime | The date and time when a button was last pressed. (Read Only) (Advanced) |
| rotary-steps | (String) | Informs about the number of rotary steps of the last rotary dial movement. (Trigger Channel) |
| rotary-steps-last-updated | DateTime | The date and time when the rotary steps were last updated. (Read Only) (Advanced) |
| motion | Switch | Shows if motion has been detected by the sensor. (Read Only) |
| motion-enabled | Switch | Supports enabling / disabling the motion sensor. (Advanced) |
| motion-last-updated | DateTime | The date and time when the motion value was last updated. (Read Only) (Advanced) |
| light-level | Number:Illuminance | Shows the current light level measured by the sensor. (Read Only) |
| light-level-last-updated | DateTime | The date and time when the light level was last updated. (Read Only) (Advanced) |
| light-level-enabled | Switch | Supports enabling / disabling the light level sensor. (Advanced) |
| temperature | Number:Temperature | Shows the current temperature measured by the sensor. (Read Only) |
| temperature-last-updated | DateTime | The date and time when the temperature was last updated. (Read Only) (Advanced) |
| temperature-enabled | Switch | Supports enabling / disabling the temperature sensor. (Advanced) |
| battery-level | Number | Shows the battery level. (Read Only) |
| battery-low | Switch | Indicates whether the battery is low or not. (Read Only) |
| last-updated | DateTime | The date and time when the thing state was last updated. (Read Only) (Advanced) |
| color-xy-only | Color | Allows access to the `color-xy` parameter of the light(s) only. Has no impact on `dimming` or `on-off` parameters. |
| dimming-only | Dimmer | Allows access to the `dimming` parameter of the light(s) only. Has no impact on `color-xy` or `on-off` parameters. |
| on-off-only | Switch | Allows access to the `on-off` parameter of the light(s) only. Has no impact on `color-xy` or `dimming` parameters. |
| Channel ID | Item Type | Description |
|-------------------------------|--------------------|---------------------------------------------------------------------------------------------------------------------|
| color | Color | Supports full color control with hue, saturation and brightness values, or brightness only, or switching on or off. |
| brightness | Dimmer | Supports control of the brightness value, or switching on or off. |
| color-temperature | Dimmer | Supports control of the color temperature in percent from cold (0%) to warm (100%). |
| color-temperature-abs | Number:Temperature | Supports control of the color temperature via a QuantityType having a temperature unit e.g. Kelvin. (Advanced) |
| switch | Switch | Supports switching the device on and off. |
| dynamics | Number:Time | Sets the duration of dynamic transitions between light states. (Advanced) |
| alert | String | Allows setting an alert on a light e.g. flashing them. (Advanced) |
| effect | String | Allows setting an effect on a light e.g. 'candle' effect. (Advanced) |
| button-last-event | (String) | Informs which button was last pressed in the device. (Trigger Channel) |
| button-last-updated | DateTime | The date and time when a button was last pressed. (Read Only) (Advanced) |
| rotary-steps | (String) | Informs about the number of rotary steps of the last rotary dial movement. (Trigger Channel) |
| rotary-steps-last-updated | DateTime | The date and time when the rotary steps were last updated. (Read Only) (Advanced) |
| motion | Switch | Shows if motion has been detected by the sensor. (Read Only) |
| motion-enabled | Switch | Supports enabling / disabling the motion sensor. (Advanced) |
| motion-last-updated | DateTime | The date and time when the motion value was last updated. (Read Only) (Advanced) |
| light-level | Number:Illuminance | Shows the current light level measured by the sensor. (Read Only) |
| light-level-last-updated | DateTime | The date and time when the light level was last updated. (Read Only) (Advanced) |
| light-level-enabled | Switch | Supports enabling / disabling the light level sensor. (Advanced) |
| temperature | Number:Temperature | Shows the current temperature measured by the sensor. (Read Only) |
| temperature-last-updated | DateTime | The date and time when the temperature was last updated. (Read Only) (Advanced) |
| temperature-enabled | Switch | Supports enabling / disabling the temperature sensor. (Advanced) |
| battery-level | Number | Shows the battery level. (Read Only) |
| battery-low | Switch | Indicates whether the battery is low or not. (Read Only) |
| last-updated | DateTime | The date and time when the thing state was last updated. (Read Only) (Advanced) |
| color-xy-only | Color | Allows access to the `color-xy` parameter of the light(s) only. Has no impact on `dimming` or `on-off` parameters. |
| dimming-only | Dimmer | Allows access to the `dimming` parameter of the light(s) only. Has no impact on `color-xy` or `on-off` parameters. |
| on-off-only | Switch | Allows access to the `on-off` parameter of the light(s) only. Has no impact on `color-xy` or `dimming` parameters. |
| security-contact | Contact | Indicates whether a security contact has been triggered. (Read Only) |
| security-contact-enabled | Switch | Supports enabling / disabling the security contact. (Advanced) |
| security-contact-last-updated | DateTime | The date and time when the security contact state was last updated. (Read Only) (Advanced) |
| security-tamper | Contact | Indicates whether a security tamper contact has been triggered. `Open` means tampering detected. (Read Only) |
| security-tamper-last-updated | DateTime | The date and time when the security tamper contact state was last updated. (Read Only) (Advanced) |
The exact list of channels in a given device is determined at run time when the system is started.
Each device reports its own live list of capabilities, and the respective list of channels is created accordingly.

View File

@ -169,6 +169,11 @@ public class HueBindingConstants {
public static final String CHANNEL_2_COLOR_XY_ONLY = "color-xy-only";
public static final String CHANNEL_2_DIMMING_ONLY = "dimming-only";
public static final String CHANNEL_2_ON_OFF_ONLY = "on-off-only";
public static final String CHANNEL_2_SECURITY_CONTACT = "security-contact";
public static final String CHANNEL_2_SECURITY_CONTACT_ENABLED = "security-contact-enabled";
public static final String CHANNEL_2_SECURITY_CONTACT_LAST_UPDATED = "security-contact-last-updated";
public static final String CHANNEL_2_SECURITY_TAMPER = "security-tamper";
public static final String CHANNEL_2_SECURITY_TAMPER_LAST_UPDATED = "security-tamper-last-updated";
// channel IDs that (optionally) support dynamics
public static final Set<String> DYNAMIC_CHANNELS = Set.of(CHANNEL_2_BRIGHTNESS, CHANNEL_2_COLOR,

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2023 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.hue.internal.dto.clip2;
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.hue.internal.dto.clip2.enums.ContactStateType;
/**
* DTO for CLIP 2 home security alarm contact.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class ContactReport {
private @NonNullByDefault({}) Instant changed;
private @NonNullByDefault({}) String state;
public ContactStateType getContactState() throws IllegalArgumentException {
return ContactStateType.valueOf(state.toUpperCase());
}
public Instant getLastChanged() {
return changed;
}
public ContactReport setLastChanged(Instant changed) {
this.changed = changed;
return this;
}
public ContactReport setContactState(String state) {
this.state = state;
return this;
}
}

View File

@ -28,17 +28,20 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
import org.openhab.binding.hue.internal.dto.clip2.enums.ButtonEventType;
import org.openhab.binding.hue.internal.dto.clip2.enums.ContactStateType;
import org.openhab.binding.hue.internal.dto.clip2.enums.EffectType;
import org.openhab.binding.hue.internal.dto.clip2.enums.ResourceType;
import org.openhab.binding.hue.internal.dto.clip2.enums.SceneRecallAction;
import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneRecallAction;
import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneState;
import org.openhab.binding.hue.internal.dto.clip2.enums.TamperStateType;
import org.openhab.binding.hue.internal.dto.clip2.enums.ZigbeeStatus;
import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
@ -103,6 +106,8 @@ public class Resource {
private @Nullable List<ResourceReference> children;
private @Nullable JsonElement status;
private @Nullable @SuppressWarnings("unused") Dynamics dynamics;
private @Nullable @SerializedName("contact_report") ContactReport contactReport;
private @Nullable @SerializedName("tamper_reports") List<TamperReport> tamperReports;
private @Nullable String state;
/**
@ -325,6 +330,20 @@ public class Resource {
return UnDefType.NULL;
}
public State getContactLastUpdatedState(ZoneId zoneId) {
ContactReport contactReport = this.contactReport;
return Objects.nonNull(contactReport)
? new DateTimeType(ZonedDateTime.ofInstant(contactReport.getLastChanged(), zoneId))
: UnDefType.NULL;
}
public State getContactState() {
ContactReport contactReport = this.contactReport;
return Objects.isNull(contactReport) ? UnDefType.NULL
: ContactStateType.CONTACT == contactReport.getContactState() ? OpenClosedType.CLOSED
: OpenClosedType.OPEN;
}
public int getControlId() {
MetaData metadata = this.metadata;
return Objects.nonNull(metadata) ? metadata.getControlId() : 0;
@ -649,6 +668,33 @@ public class Resource {
return new JsonObject();
}
public State getTamperLastUpdatedState(ZoneId zoneId) {
TamperReport report = getTamperReportsLatest();
return Objects.nonNull(report) ? new DateTimeType(ZonedDateTime.ofInstant(report.getLastChanged(), zoneId))
: UnDefType.NULL;
}
/**
* The the Hue bridge could return its raw list of tamper reports in any order, so sort the list (latest entry
* first) according to the respective 'changed' instant and return the first entry i.e. the latest changed entry.
*
* @return the latest changed tamper report
*/
private @Nullable TamperReport getTamperReportsLatest() {
List<TamperReport> reports = this.tamperReports;
return Objects.nonNull(reports)
? reports.stream().sorted((e1, e2) -> e2.getLastChanged().compareTo(e1.getLastChanged())).findFirst()
.orElse(null)
: null;
}
public State getTamperState() {
TamperReport report = getTamperReportsLatest();
return Objects.nonNull(report)
? TamperStateType.TAMPERED == report.getTamperState() ? OpenClosedType.OPEN : OpenClosedType.CLOSED
: UnDefType.NULL;
}
public @Nullable Temperature getTemperature() {
return temperature;
}
@ -736,6 +782,11 @@ public class Resource {
return this;
}
public Resource setContactReport(ContactReport contactReport) {
this.contactReport = contactReport;
return this;
}
public Resource setDimming(Dimming dimming) {
this.dimming = dimming;
return this;
@ -815,6 +866,11 @@ public class Resource {
return this;
}
public Resource setTamperReports(List<TamperReport> tamperReports) {
this.tamperReports = tamperReports;
return this;
}
public Resource setTimedEffects(TimedEffects timedEffects) {
this.timedEffects = timedEffects;
return this;

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2023 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.hue.internal.dto.clip2;
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.hue.internal.dto.clip2.enums.TamperStateType;
/**
* DTO for CLIP 2 home security tamper switch.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class TamperReport {
private @NonNullByDefault({}) Instant changed;
private @NonNullByDefault({}) String state;
public Instant getLastChanged() {
return changed;
}
public TamperStateType getTamperState() throws IllegalArgumentException {
return TamperStateType.valueOf(state.toUpperCase());
}
public TamperReport setLastChanged(Instant changed) {
this.changed = changed;
return this;
}
public TamperReport setTamperState(String state) {
this.state = state;
return this;
}
}

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2023 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.hue.internal.dto.clip2.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Enum for security contact states.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public enum ContactStateType {
NO_CONTACT,
CONTACT
}

View File

@ -31,6 +31,8 @@ public enum ResourceType {
BRIDGE,
BRIDGE_HOME,
BUTTON,
CAMERA_MOTION,
CONTACT,
DEVICE,
DEVICE_POWER,
ENTERTAINMENT,
@ -47,6 +49,7 @@ public enum ResourceType {
ROOM,
RELATIVE_ROTARY,
SCENE,
TAMPER,
SMART_SCENE,
TEMPERATURE,
ZGP_CONNECTIVITY,

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2023 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.hue.internal.dto.clip2.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Enum for tamper switch states.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public enum TamperStateType {
NOT_TAMPERED,
TAMPERED
}

View File

@ -433,6 +433,10 @@ public class Clip2ThingHandler extends BaseThingHandler {
putResource = new Resource(ResourceType.LIGHT_LEVEL).setEnabled(command);
break;
case CHANNEL_2_SECURITY_CONTACT_ENABLED:
putResource = new Resource(ResourceType.CONTACT).setEnabled(command);
break;
case CHANNEL_2_SCENE:
if (command instanceof StringType) {
Resource scene = sceneResourceEntries.get(((StringType) command).toString());
@ -888,6 +892,7 @@ public class Clip2ThingHandler extends BaseThingHandler {
break;
case MOTION:
case CAMERA_MOTION:
updateState(CHANNEL_2_MOTION, resource.getMotionState(), fullUpdate);
updateState(CHANNEL_2_MOTION_LAST_UPDATED,
resource.getMotionLastUpdatedState(timeZoneProvider.getTimeZone()), fullUpdate);
@ -920,6 +925,19 @@ public class Clip2ThingHandler extends BaseThingHandler {
updateState(CHANNEL_2_SCENE, resource.getSceneState(), fullUpdate);
break;
case CONTACT:
updateState(CHANNEL_2_SECURITY_CONTACT, resource.getContactState(), fullUpdate);
updateState(CHANNEL_2_SECURITY_CONTACT_LAST_UPDATED,
resource.getContactLastUpdatedState(timeZoneProvider.getTimeZone()), fullUpdate);
updateState(CHANNEL_2_SECURITY_CONTACT_ENABLED, resource.getEnabledState(), fullUpdate);
break;
case TAMPER:
updateState(CHANNEL_2_SECURITY_TAMPER, resource.getTamperState(), fullUpdate);
updateState(CHANNEL_2_SECURITY_TAMPER_LAST_UPDATED,
resource.getTamperLastUpdatedState(timeZoneProvider.getTimeZone()), fullUpdate);
break;
case SMART_SCENE:
updateState(CHANNEL_2_SCENE, resource.getSmartSceneState(), fullUpdate);
break;

View File

@ -55,6 +55,15 @@ thing-type.hue.device.channel.motion-last-updated.description = The date and tim
thing-type.hue.device.channel.on-off-only.description = Set the on/off parameter of the light without changing other state parameters.
thing-type.hue.device.channel.rotary-steps-last-updated.label = Rotary Steps Last Updated
thing-type.hue.device.channel.rotary-steps-last-updated.description = The date and time when the rotary steps were last updated.
thing-type.hue.device.channel.security-contact.label = Security Contact
thing-type.hue.device.channel.security-contact.description = Open or closed state of the contact.
thing-type.hue.device.channel.security-contact-enabled.description = Security contact enabled.
thing-type.hue.device.channel.security-contact-last-updated.label = Security Contact Last Updated
thing-type.hue.device.channel.security-contact-last-updated.description = The date and time when the contact state was last updated.
thing-type.hue.device.channel.security-tamper.label = Security Tamper Contact
thing-type.hue.device.channel.security-tamper.description = Tamper or no tamper state of the sensor.
thing-type.hue.device.channel.security-tamper-last-updated.label = Tamper Contact Last Updated
thing-type.hue.device.channel.security-tamper-last-updated.description = The date and time when the tamper contact state was last updated.
thing-type.hue.device.channel.temperature.label = Temperature
thing-type.hue.device.channel.temperature.description = Temperature at the sensor location.
thing-type.hue.device.channel.temperature-enabled.description = Temperature sensor enabled.
@ -183,6 +192,8 @@ channel-type.hue.rotary-steps.description = The last 'steps' value (e.g. +/-30)
channel-type.hue.scene-v2.label = Scene
channel-type.hue.scene.label = Scene
channel-type.hue.scene.description = The scene channel allows recalling a scene to all lights that belong to the scene.
channel-type.hue.security-contact.label = Open/Closed
channel-type.hue.security-tamper.label = Normal/Tamper
channel-type.hue.sensor-enabled.label = Sensor Enabled
channel-type.hue.status.label = Status
channel-type.hue.status.description = Status of CLIP sensor.

View File

@ -62,6 +62,25 @@
<channel id="temperature-enabled" typeId="sensor-enabled">
<description>Temperature sensor enabled.</description>
</channel>
<channel id="security-contact" typeId="security-contact">
<label>Security Contact</label>
<description>Open or closed state of the contact.</description>
</channel>
<channel id="security-contact-enabled" typeId="sensor-enabled">
<description>Security contact enabled.</description>
</channel>
<channel id="security-contact-last-updated" typeId="last-updated-v2">
<label>Security Contact Last Updated</label>
<description>The date and time when the contact state was last updated.</description>
</channel>
<channel id="security-tamper" typeId="security-tamper">
<label>Security Tamper Contact</label>
<description>Tamper or no tamper state of the sensor.</description>
</channel>
<channel id="security-tamper-last-updated" typeId="last-updated-v2">
<label>Tamper Contact Last Updated</label>
<description>The date and time when the tamper contact state was last updated.</description>
</channel>
<channel id="battery-level" typeId="system.battery-level"/>
<channel id="battery-low" typeId="system.low-battery"/>
<channel id="last-updated" typeId="last-updated-v2"/>

View File

@ -256,4 +256,16 @@
<category>Switch</category>
</channel-type>
<channel-type id="security-contact">
<item-type>Contact</item-type>
<label>Open/Closed</label>
<category>Lock</category>
</channel-type>
<channel-type id="security-tamper">
<item-type>Contact</item-type>
<label>Normal/Tamper</label>
<category>Siren</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -21,6 +21,7 @@ import java.lang.reflect.Field;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -31,6 +32,7 @@ import org.junit.jupiter.api.Test;
import org.openhab.binding.hue.internal.dto.clip2.ActionEntry;
import org.openhab.binding.hue.internal.dto.clip2.Alerts;
import org.openhab.binding.hue.internal.dto.clip2.Button;
import org.openhab.binding.hue.internal.dto.clip2.ContactReport;
import org.openhab.binding.hue.internal.dto.clip2.Dimming;
import org.openhab.binding.hue.internal.dto.clip2.Effects;
import org.openhab.binding.hue.internal.dto.clip2.Event;
@ -46,6 +48,7 @@ import org.openhab.binding.hue.internal.dto.clip2.ResourceReference;
import org.openhab.binding.hue.internal.dto.clip2.Resources;
import org.openhab.binding.hue.internal.dto.clip2.Rotation;
import org.openhab.binding.hue.internal.dto.clip2.RotationEvent;
import org.openhab.binding.hue.internal.dto.clip2.TamperReport;
import org.openhab.binding.hue.internal.dto.clip2.Temperature;
import org.openhab.binding.hue.internal.dto.clip2.TimedEffects;
import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
@ -63,6 +66,7 @@ import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
@ -707,6 +711,91 @@ class Clip2DtoTest {
}
@Test
void testSecurityContact() {
String json = load(ResourceType.CONTACT.name().toLowerCase());
Resources resources = GSON.fromJson(json, Resources.class);
assertNotNull(resources);
List<Resource> list = resources.getResources();
assertNotNull(list);
assertEquals(1, list.size());
Resource resource = list.get(0);
assertEquals(ResourceType.CONTACT, resource.getType());
assertEquals(OpenClosedType.CLOSED, resource.getContactState());
assertEquals(new DateTimeType("2023-10-10T19:10:55.919Z"),
resource.getContactLastUpdatedState(ZoneId.of("UTC")));
resource.setContactReport(new ContactReport().setLastChanged(Instant.now()).setContactState("no_contact"));
assertEquals(OpenClosedType.OPEN, resource.getContactState());
assertTrue(resource.getContactLastUpdatedState(ZoneId.of("UTC")) instanceof DateTimeType);
}
@Test
void testSecurityTamper() {
String json = load(ResourceType.TAMPER.name().toLowerCase());
Resources resources = GSON.fromJson(json, Resources.class);
assertNotNull(resources);
List<Resource> list = resources.getResources();
assertNotNull(list);
assertEquals(1, list.size());
Resource resource = list.get(0);
assertEquals(ResourceType.TAMPER, resource.getType());
assertEquals(OpenClosedType.CLOSED, resource.getTamperState());
assertEquals(new DateTimeType("2023-01-01T00:00:00.001Z"),
resource.getTamperLastUpdatedState(ZoneId.of("UTC")));
Instant start = Instant.now();
List<TamperReport> tamperReports;
State state;
tamperReports = new ArrayList<>();
tamperReports.add(new TamperReport().setTamperState("not_tampered").setLastChanged(start));
resource.setTamperReports(tamperReports);
assertEquals(OpenClosedType.CLOSED, resource.getTamperState());
state = resource.getTamperLastUpdatedState(ZoneId.of("UTC"));
assertTrue(state instanceof DateTimeType);
assertEquals(start, ((DateTimeType) state).getInstant());
tamperReports = new ArrayList<>();
tamperReports.add(new TamperReport().setTamperState("not_tampered").setLastChanged(start));
tamperReports.add(new TamperReport().setTamperState("tampered").setLastChanged(start.plusSeconds(1)));
resource.setTamperReports(tamperReports);
assertEquals(OpenClosedType.OPEN, resource.getTamperState());
state = resource.getTamperLastUpdatedState(ZoneId.of("UTC"));
assertTrue(state instanceof DateTimeType);
assertEquals(start.plusSeconds(1), ((DateTimeType) state).getInstant());
tamperReports = new ArrayList<>();
tamperReports.add(new TamperReport().setTamperState("not_tampered").setLastChanged(start));
tamperReports.add(new TamperReport().setTamperState("tampered").setLastChanged(start.plusSeconds(1)));
tamperReports.add(new TamperReport().setTamperState("not_tampered").setLastChanged(start.plusSeconds(2)));
resource.setTamperReports(tamperReports);
assertEquals(OpenClosedType.CLOSED, resource.getTamperState());
state = resource.getTamperLastUpdatedState(ZoneId.of("UTC"));
assertTrue(state instanceof DateTimeType);
assertEquals(start.plusSeconds(2), ((DateTimeType) state).getInstant());
}
@Test
void testCameraMotion() {
String json = load(ResourceType.CAMERA_MOTION.name().toLowerCase());
Resources resources = GSON.fromJson(json, Resources.class);
assertNotNull(resources);
List<Resource> list = resources.getResources();
assertNotNull(list);
assertEquals(1, list.size());
Resource resource = list.get(0);
assertEquals(ResourceType.CAMERA_MOTION, resource.getType());
Boolean enabled = resource.getEnabled();
assertNotNull(enabled);
assertTrue(enabled);
assertEquals(OnOffType.ON, resource.getMotionState());
assertEquals(new DateTimeType("2020-04-01T20:04:30.395Z"),
resource.getMotionLastUpdatedState(ZoneId.of("UTC")));
}
void testFixedEffectSetter() {
Resource source;
Resource target;

View File

@ -0,0 +1,28 @@
{
"errors": [],
"data": [
{
"id": "00000000-0000-0000-0000-000000000005",
"id_v1": "/sensors/5",
"owner": {
"rid": "00000000-0000-0000-0000-000000000000",
"rtype": "device"
},
"enabled": true,
"motion": {
"motion": false,
"motion_valid": true,
"motion_report": {
"changed": "2020-04-01T20:04:30.395Z",
"motion": true
}
},
"sensitivity": {
"status": "set",
"sensitivity": 2,
"sensitivity_max": 4
},
"type": "camera_motion"
}
]
}

View File

@ -0,0 +1,19 @@
{
"errors": [
],
"data": [
{
"id": "bcaee909-1b37-454b-814d-9928776ad350",
"owner": {
"rid": "faac7940-a303-4e8e-9f06-075fffb7229c",
"rtype": "device"
},
"enabled": true,
"contact_report": {
"changed": "2023-10-10T19:10:55.919Z",
"state": "contact"
},
"type": "contact"
}
]
}

View File

@ -0,0 +1,31 @@
{
"errors": [
],
"data": [
{
"id": "6c2ef541-fe03-4cae-ac60-3edcaa93b33e",
"owner": {
"rid": "faac7940-a303-4e8e-9f06-075fffb7229c",
"rtype": "device"
},
"tamper_reports": [
{
"changed": "1970-01-01T00:00:00.000Z",
"source": "battery_door",
"state": "not_tampered"
},
{
"changed": "2023-01-01T00:00:00.001Z",
"source": "battery_door",
"state": "not_tampered"
},
{
"changed": "2023-01-01T00:00:00.000Z",
"source": "battery_door",
"state": "tampered"
}
],
"type": "tamper"
}
]
}