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.
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,
--- /dev/null
+/**
+ * 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;
+ }
+}
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;
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;
/**
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;
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;
}
return this;
}
+ public Resource setContactReport(ContactReport contactReport) {
+ this.contactReport = contactReport;
+ return this;
+ }
+
public Resource setDimming(Dimming dimming) {
this.dimming = dimming;
return this;
return this;
}
+ public Resource setTamperReports(List<TamperReport> tamperReports) {
+ this.tamperReports = tamperReports;
+ return this;
+ }
+
public Resource setTimedEffects(TimedEffects timedEffects) {
this.timedEffects = timedEffects;
return this;
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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
+}
BRIDGE,
BRIDGE_HOME,
BUTTON,
+ CAMERA_MOTION,
+ CONTACT,
DEVICE,
DEVICE_POWER,
ENTERTAINMENT,
ROOM,
RELATIVE_ROTARY,
SCENE,
+ TAMPER,
SMART_SCENE,
TEMPERATURE,
ZGP_CONNECTIVITY,
--- /dev/null
+/**
+ * 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
+}
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());
break;
case MOTION:
+ case CAMERA_MOTION:
updateState(CHANNEL_2_MOTION, resource.getMotionState(), fullUpdate);
updateState(CHANNEL_2_MOTION_LAST_UPDATED,
resource.getMotionLastUpdatedState(timeZoneProvider.getTimeZone()), fullUpdate);
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;
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.
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.
<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"/>
<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>
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;
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;
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;
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;
}
@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;
--- /dev/null
+{
+ "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"
+ }
+ ]
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+}