Java tutorial
/* * Copyright 2015 Open mHealth * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openmhealth.shim.withings.mapper; import com.fasterxml.jackson.databind.JsonNode; import org.openmhealth.schema.domain.omh.*; import java.time.Instant; import java.time.OffsetDateTime; import java.util.HashMap; import java.util.Map; import java.util.Optional; import static java.time.ZoneId.of; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asOptionalLong; import static org.openmhealth.shim.common.mapper.JsonNodeMappingSupport.asRequiredLong; /** * A mapper from Withings Sleep Summary endpoint responses (/sleep?action=getsummary) to {@link SleepDuration} * objects. * * @author Chris Schaefbauer * @see <a href="http://oauth.withings.com/api/doc#api-Measure-get_sleep_summary">Sleep Summary API documentation</a> */ public class WithingsSleepDurationDataPointMapper extends WithingsListDataPointMapper<SleepDuration> { /** * Maps an individual list node from the array in the Withings sleep summary endpoint response into a {@link * SleepDuration} data point. * * @param node activity node from the array "series" contained in the "body" of the endpoint response * @return a {@link DataPoint} object containing a {@link SleepDuration} measure with the appropriate values from * the JSON node parameter, wrapped as an {@link Optional} */ @Override Optional<DataPoint<SleepDuration>> asDataPoint(JsonNode node) { Long lightSleepInSeconds = asRequiredLong(node, "data.lightsleepduration"); Long deepSleepInSeconds = asRequiredLong(node, "data.deepsleepduration"); Long remSleepInSeconds = asRequiredLong(node, "data.remsleepduration"); Long totalSleepInSeconds = lightSleepInSeconds + deepSleepInSeconds + remSleepInSeconds; SleepDuration.Builder sleepDurationBuilder = new SleepDuration.Builder( new DurationUnitValue(DurationUnit.SECOND, totalSleepInSeconds)); Optional<Long> startDateInEpochSeconds = asOptionalLong(node, "startdate"); Optional<Long> endDateInEpochSeconds = asOptionalLong(node, "enddate"); if (startDateInEpochSeconds.isPresent() && endDateInEpochSeconds.isPresent()) { OffsetDateTime offsetStartDateTime = OffsetDateTime .ofInstant(Instant.ofEpochSecond(startDateInEpochSeconds.get()), of("Z")); OffsetDateTime offsetEndDateTime = OffsetDateTime .ofInstant(Instant.ofEpochSecond(endDateInEpochSeconds.get()), of("Z")); sleepDurationBuilder.setEffectiveTimeFrame( TimeInterval.ofStartDateTimeAndEndDateTime(offsetStartDateTime, offsetEndDateTime)); } Optional<Long> externalId = asOptionalLong(node, "id"); Optional<Long> modelId = asOptionalLong(node, "model"); String modelName = null; if (modelId.isPresent()) { modelName = SleepDeviceTypes.valueOf(modelId.get()); } SleepDuration sleepDuration = sleepDurationBuilder.build(); Optional<Long> wakeupCount = asOptionalLong(node, "data.wakeupcount"); if (wakeupCount.isPresent()) { sleepDuration.setAdditionalProperty("wakeup_count", new Integer(wakeupCount.get().intValue())); } // These sleep phase values are Withings platform-specific, so we pass them through as additionalProperties to // ensure we keep relevant platform specific values. Should be interpreted according to Withings API spec sleepDuration.setAdditionalProperty("light_sleep_duration", new DurationUnitValue(DurationUnit.SECOND, lightSleepInSeconds)); sleepDuration.setAdditionalProperty("deep_sleep_duration", new DurationUnitValue(DurationUnit.SECOND, deepSleepInSeconds)); sleepDuration.setAdditionalProperty("rem_sleep_duration", new DurationUnitValue(DurationUnit.SECOND, remSleepInSeconds)); // This is an additional piece of information captured by Withings devices around sleep and should be // interpreted according to the Withings API specification. We do not capture durationtowakeup or // wakeupduration properties from the Withings API because it is unclear the distinction between them and we // aim to avoid creating more ambiguity through passing through these properties Optional<Long> timeToSleepValue = asOptionalLong(node, "data.durationtosleep"); if (timeToSleepValue.isPresent()) { sleepDuration.setAdditionalProperty("duration_to_sleep", new DurationUnitValue(DurationUnit.SECOND, timeToSleepValue.get())); } return Optional.of(newDataPoint(sleepDuration, externalId.orElse(null), true, modelName)); } @Override String getListNodeName() { return "series"; } // TODO clean this up public enum SleepDeviceTypes { Pulse(16), Aura(32); private long deviceId; private static Map<Long, String> map = new HashMap<Long, String>(); static { for (SleepDeviceTypes sleepDeviceTypeName : SleepDeviceTypes.values()) { map.put(sleepDeviceTypeName.deviceId, sleepDeviceTypeName.name()); } } SleepDeviceTypes(final long deviceId) { this.deviceId = deviceId; } /** * Returns the string device name for a device ID * * @param deviceId the id number for the device contained within the Withings API response datapoint * @return common name of the device (e.g., Pulse, Aura) */ public static String valueOf(long deviceId) { return map.get(deviceId); } } }