From 2b367d45f3f7f2c99d1e238d51aaa08b71d320f5 Mon Sep 17 00:00:00 2001 From: James Woglom Date: Tue, 10 Dec 2024 20:35:17 -0500 Subject: [PATCH] eventparser: use eventID consistently as seqNum --- tconnectsync/eventparser/build_events.py | 6 +- tconnectsync/eventparser/events.py | 336 +++++++++++++++--- tconnectsync/eventparser/raw_event.py | 6 +- tconnectsync/nightscout.py | 27 ++ tconnectsync/parser/nightscout.py | 14 + tconnectsync/sync/tandemsource/autoupdate.py | 12 +- tconnectsync/sync/tandemsource/cli_helpers.py | 24 ++ tconnectsync/sync/tandemsource/process.py | 12 +- .../sync/tandemsource/process_alarm.py | 4 +- .../sync/tandemsource/process_basal.py | 4 +- .../sync/tandemsource/process_basal_resume.py | 2 +- .../tandemsource/process_basal_suspension.py | 2 +- .../sync/tandemsource/process_bolus.py | 8 +- .../sync/tandemsource/process_cartridge.py | 6 +- .../sync/tandemsource/process_cgm_alert.py | 6 +- .../sync/tandemsource/process_cgm_reading.py | 2 +- .../process_cgm_start_join_stop.py | 6 +- .../tandemsource/process_device_status.py | 76 ++++ .../sync/tandemsource/process_user_mode.py | 12 +- 19 files changed, 465 insertions(+), 100 deletions(-) create mode 100644 tconnectsync/sync/tandemsource/cli_helpers.py create mode 100644 tconnectsync/sync/tandemsource/process_device_status.py diff --git a/tconnectsync/eventparser/build_events.py b/tconnectsync/eventparser/build_events.py index 018abdd..f2fcdad 100644 --- a/tconnectsync/eventparser/build_events.py +++ b/tconnectsync/eventparser/build_events.py @@ -61,9 +61,13 @@ class {name}(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + ''' def firstLower(text): diff --git a/tconnectsync/eventparser/events.py b/tconnectsync/eventparser/events.py index bcc372d..5a11a51 100644 --- a/tconnectsync/eventparser/events.py +++ b/tconnectsync/eventparser/events.py @@ -81,9 +81,13 @@ class LidBasalRateChange(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAlertActivated(BaseEvent): @@ -257,9 +261,13 @@ class LidAlertActivated(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAlarmActivated(BaseEvent): @@ -435,9 +443,13 @@ class LidAlarmActivated(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidMalfunctionActivated(BaseEvent): @@ -472,9 +484,13 @@ class LidMalfunctionActivated(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidPumpingSuspended(BaseEvent): @@ -530,9 +546,13 @@ class LidPumpingSuspended(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidPumpingResumed(BaseEvent): @@ -561,9 +581,13 @@ class LidPumpingResumed(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidTimeChanged(BaseEvent): @@ -595,9 +619,13 @@ class LidTimeChanged(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidDateChanged(BaseEvent): @@ -629,9 +657,13 @@ class LidDateChanged(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBgReadingTaken(BaseEvent): @@ -749,9 +781,13 @@ class LidBgReadingTaken(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolusCompleted(BaseEvent): @@ -817,9 +853,13 @@ class LidBolusCompleted(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolexCompleted(BaseEvent): @@ -885,9 +925,13 @@ class LidBolexCompleted(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAlertCleared(BaseEvent): @@ -1055,9 +1099,13 @@ class LidAlertCleared(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAlarmCleared(BaseEvent): @@ -1224,9 +1272,13 @@ class LidAlarmCleared(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCartridgeFilled(BaseEvent): @@ -1255,9 +1307,13 @@ class LidCartridgeFilled(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidShelfMode(BaseEvent): @@ -1298,9 +1354,13 @@ class LidShelfMode(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolusActivated(BaseEvent): @@ -1352,9 +1412,13 @@ class LidBolusActivated(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolexActivated(BaseEvent): @@ -1406,9 +1470,13 @@ class LidBolexActivated(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidDataLogCorruption(BaseEvent): @@ -1437,9 +1505,13 @@ class LidDataLogCorruption(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCannulaFilled(BaseEvent): @@ -1489,9 +1561,13 @@ class LidCannulaFilled(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidTubingFilled(BaseEvent): @@ -1544,9 +1620,13 @@ class LidTubingFilled(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolusRequestedMsg1(BaseEvent): @@ -1633,9 +1713,13 @@ class LidBolusRequestedMsg1(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolusRequestedMsg2(BaseEvent): @@ -1768,9 +1852,13 @@ class LidBolusRequestedMsg2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolusRequestedMsg3(BaseEvent): @@ -1805,9 +1893,13 @@ class LidBolusRequestedMsg3(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidNewDay(BaseEvent): @@ -1839,9 +1931,13 @@ class LidNewDay(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidArmInit(BaseEvent): @@ -1876,9 +1972,13 @@ class LidArmInit(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidPlgsPeriodic(BaseEvent): @@ -2051,9 +2151,13 @@ class LidPlgsPeriodic(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertActivated(BaseEvent): @@ -2117,9 +2221,13 @@ class LidCgmAlertActivated(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertCleared(BaseEvent): @@ -2174,9 +2282,13 @@ class LidCgmAlertCleared(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidVersionInfo(BaseEvent): @@ -2211,9 +2323,13 @@ class LidVersionInfo(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidUpdateStatus(BaseEvent): @@ -2274,9 +2390,13 @@ class LidUpdateStatus(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmStartSessionGx(BaseEvent): @@ -2308,9 +2428,13 @@ class LidCgmStartSessionGx(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmJoinSessionGx(BaseEvent): @@ -2384,9 +2508,13 @@ class LidCgmJoinSessionGx(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmStopSessionGx(BaseEvent): @@ -2463,9 +2591,13 @@ class LidCgmStopSessionGx(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAaUserModeChange(BaseEvent): @@ -2682,9 +2814,13 @@ class LidAaUserModeChange(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAaPcmChange(BaseEvent): @@ -2861,9 +2997,13 @@ class LidAaPcmChange(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmDataGxb(BaseEvent): @@ -2992,9 +3132,13 @@ class LidCgmDataGxb(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBasalDelivery(BaseEvent): @@ -3055,9 +3199,13 @@ class LidBasalDelivery(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidBolusDelivery(BaseEvent): @@ -3183,9 +3331,13 @@ class LidBolusDelivery(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidVersionsA(BaseEvent): @@ -3220,9 +3372,13 @@ class LidVersionsA(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidAaDailyStatus(BaseEvent): @@ -3317,9 +3473,13 @@ class LidAaDailyStatus(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertActivatedDex(BaseEvent): @@ -3406,9 +3566,13 @@ class LidCgmAlertActivatedDex(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertClearedDex(BaseEvent): @@ -3486,9 +3650,13 @@ class LidCgmAlertClearedDex(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertAckDex(BaseEvent): @@ -3587,9 +3755,13 @@ class LidCgmAlertAckDex(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmDataFsl2(BaseEvent): @@ -3752,9 +3924,13 @@ class LidCgmDataFsl2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmJoinSessionG7(BaseEvent): @@ -3783,9 +3959,13 @@ class LidCgmJoinSessionG7(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmDataG7(BaseEvent): @@ -3954,9 +4134,13 @@ class LidCgmDataG7(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmStartSessionFsl2(BaseEvent): @@ -3985,9 +4169,13 @@ class LidCgmStartSessionFsl2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmStopSessionFsl2(BaseEvent): @@ -4022,9 +4210,13 @@ class LidCgmStopSessionFsl2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmJoinSessionFsl2(BaseEvent): @@ -4059,9 +4251,13 @@ class LidCgmJoinSessionFsl2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmStopSessionG7(BaseEvent): @@ -4102,9 +4298,13 @@ class LidCgmStopSessionG7(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertActivatedFsl2(BaseEvent): @@ -4189,9 +4389,13 @@ class LidCgmAlertActivatedFsl2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCgmAlertClearedFsl2(BaseEvent): @@ -4267,9 +4471,13 @@ class LidCgmAlertClearedFsl2(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidDailyBasal(BaseEvent): @@ -4310,9 +4518,13 @@ class LidDailyBasal(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidCarbsEntered(BaseEvent): @@ -4338,9 +4550,13 @@ class LidCarbsEntered(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidUsbConnected(BaseEvent): @@ -4366,9 +4582,13 @@ class LidUsbConnected(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + @dataclass class LidUsbDisconnected(BaseEvent): @@ -4394,9 +4614,13 @@ class LidUsbDisconnected(BaseEvent): return self.raw.timestamp @property - def eventId(self): + def seqNum(self): return self.raw.seqNum + @property + def eventId(self): + return self.ID + EVENT_IDS = { 3: LidBasalRateChange, 4: LidAlertActivated, diff --git a/tconnectsync/eventparser/raw_event.py b/tconnectsync/eventparser/raw_event.py index 6a35d69..4bb67cc 100644 --- a/tconnectsync/eventparser/raw_event.py +++ b/tconnectsync/eventparser/raw_event.py @@ -26,7 +26,7 @@ class BaseEvent: raise NotImplemented @dataclass -class RawEvent(BaseEvent): +class RawEvent: source: int id: int timestampRaw: int @@ -63,8 +63,4 @@ class RawEvent(BaseEvent): def eventTimestamp(self): return self.timestamp - @property - def raw(self): - return self - diff --git a/tconnectsync/nightscout.py b/tconnectsync/nightscout.py index 93212cb..0155713 100644 --- a/tconnectsync/nightscout.py +++ b/tconnectsync/nightscout.py @@ -154,6 +154,33 @@ class NightscoutApi: else: raise e + def last_uploaded_devicestatus(self, time_start=None, time_end=None): + def internal(t_to_space): + dateFilter = time_range('created_at', time_start, time_end, t_to_space=t_to_space) + latest = requests.get(urljoin(self.url, 'api/v1/devicestatus?find[device]=' + urllib.parse.quote(ENTERED_BY) + dateFilter + '&ts=' + str(time.time())), headers={ + 'api-secret': hashlib.sha1(self.secret.encode()).hexdigest() + }, verify=self.verify) + if latest.status_code != 200: + raise ApiException(latest.status_code, "Nightscout devicestatus %s response: %s" % (latest.status_code, latest.text)) + + j = latest.json() + if j and len(j) > 0: + return j[0] + return None + + try: + ret = internal(False) + if ret is None and (time_start or time_end): + ret = internal(True) + if ret is not None: + logger.warning("devicestatus with activityType=%s time_start=%s time_end=%s only returned data when timestamps contained a space" % (activityType, time_start, time_end)) + return ret + except requests.exceptions.ConnectionError as e: + if self.ignore_conn_errors: + logger.warn('Ignoring ConnectionError because ignore_conn_errors=true', e) + else: + raise e + """ Returns general status information about the Nightscout server. """ diff --git a/tconnectsync/parser/nightscout.py b/tconnectsync/parser/nightscout.py index 781321e..8b533ff 100644 --- a/tconnectsync/parser/nightscout.py +++ b/tconnectsync/parser/nightscout.py @@ -194,6 +194,20 @@ class NightscoutEntry: "pump_event_id": pump_event_id } + @staticmethod + def devicestatus(created_at, batteryVoltage, batteryString, pump_event_id=""): + return { + "device": ENTERED_BY, + "created_at": created_at, + "pump": { + "battery": { + "voltage": float(batteryVoltage), + "string": batteryString + }, + }, + "pump_event_id": pump_event_id + } + # Tandem-scraped profile to Nightscout profile store entry @staticmethod def profile_store(profile: Profile, device_settings: DeviceSettings) -> dict: diff --git a/tconnectsync/sync/tandemsource/autoupdate.py b/tconnectsync/sync/tandemsource/autoupdate.py index 5c07183..6e2b9d6 100644 --- a/tconnectsync/sync/tandemsource/autoupdate.py +++ b/tconnectsync/sync/tandemsource/autoupdate.py @@ -18,7 +18,7 @@ class TandemSourceAutoupdate: self.last_max_date_with_events = None self.last_event_time = 0 self.last_attempt_time = 0 - self.last_event_index = None + self.last_event_seqnum = None self.time_diffs_between_attempts = [] self.time_diffs_between_updates = [] @@ -42,7 +42,7 @@ class TandemSourceAutoupdate: tconnectDevice = ChooseDevice(self.secret, tconnect).choose() - event_id = None + event_seqnum = None cur_max_date_with_events = arrow.get(tconnectDevice['maxDateWithEvents']).float_timestamp if not self.last_max_date_with_events or cur_max_date_with_events > self.last_max_date_with_events: logger.info('New reported tandemsource data. (cur_max_date: %s last_max_date: %s)' % (cur_max_date_with_events, self.last_max_date_with_events)) @@ -50,20 +50,20 @@ class TandemSourceAutoupdate: if pretend: logger.info('Would update now if not in pretend mode') else: - added, event_id = ProcessTimeRange(tconnect, nightscout, tconnectDevice, pretend, features=features).process(time_start, time_end) + added, event_seqnum = ProcessTimeRange(tconnect, nightscout, tconnectDevice, pretend, features=features).process(time_start, time_end) logger.info('Added %d items from ProcessTimeRange' % added) self.last_successful_process_time_range = now # Track the time it took to find a new event between runs, # but skip this calculation the first process cycle (since # we don't know at what exact point the event index changed) - if self.last_event_index: + if self.last_event_seqnum: self.time_diffs_between_updates.append(now - self.last_max_date_with_events) logger.debug('Updating tracking of time since last update: %s' % self.time_diffs_between_updates) # Mark the last event index uploaded from the pump and timestamp - if event_id: - self.last_event_index = event_id + if event_seqnum: + self.last_event_seqnum = event_seqnum self.last_event_time = now self.last_max_date_with_events = cur_max_date_with_events self.last_attempt_time = now diff --git a/tconnectsync/sync/tandemsource/cli_helpers.py b/tconnectsync/sync/tandemsource/cli_helpers.py new file mode 100644 index 0000000..f1cb132 --- /dev/null +++ b/tconnectsync/sync/tandemsource/cli_helpers.py @@ -0,0 +1,24 @@ +from ...features import DEFAULT_FEATURES +from ...eventparser.generic import Events, decode_raw_events, EVENT_LEN +from .choose_device import ChooseDevice +from .process import ProcessTimeRange +from ...api import TConnectApi +from ... import secret + +import datetime +import logging + +logger = logging.getLogger(__name__) + +def fetch_oneshot(username, password, time_start=None, time_end=None): + tconnect = TConnectApi(username, password) + if not time_start and not time_end: + time_end = datetime.datetime.now() + time_start = time_end - datetime.timedelta(days=1) + + tconnectDevice = ChooseDevice(secret, tconnect).choose() + pump_events_raw = tconnect.tandemsource.pump_events_raw(tconnectDevice['tconnectDeviceId'], time_start, time_end) + pump_events_decoded = decode_raw_events(pump_events_raw) + logger.info(f"Read {len(pump_events_decoded)} bytes (est. {len(pump_events_decoded)/EVENT_LEN} events)") + + return list(Events(pump_events_decoded)) \ No newline at end of file diff --git a/tconnectsync/sync/tandemsource/process.py b/tconnectsync/sync/tandemsource/process.py index e7c2504..2638a1b 100644 --- a/tconnectsync/sync/tandemsource/process.py +++ b/tconnectsync/sync/tandemsource/process.py @@ -55,18 +55,18 @@ class ProcessTimeRange: events_first_time = None events_last_time = None - last_event_id = None # aka seqnum + last_event_seqnum = None for_eventclass = collections.defaultdict(list) for event in events: if not events_first_time: events_first_time = event.eventTimestamp if not events_last_time: events_last_time = event.eventTimestamp - if not last_event_id: - last_event_id = event.eventId + if not last_event_seqnum: + last_event_seqnum = event.seqNum events_first_time = min(events_first_time, event.eventTimestamp) events_last_time = max(events_last_time, event.eventTimestamp) - last_event_id = max(event.eventId, last_event_id) + last_event_seqnum = max(event.seqNum, last_event_seqnum) clazz = EventClass.for_event(event) if clazz: @@ -97,6 +97,6 @@ class ProcessTimeRange: else: logger.info("Skipping %s, is not enabled from features %s" % (updater_class.__name__, self.features)) - logger.info("Processed %d events. Last event ID seen: %d" % (processed_count if processed_count else 0, last_event_id if last_event_id else -1)) - return processed_count, last_event_id + logger.info("Processed %d events. Last event ID seen: %d" % (processed_count if processed_count else 0, last_event_seqnum if last_event_seqnum else -1)) + return processed_count, last_event_seqnum diff --git a/tconnectsync/sync/tandemsource/process_alarm.py b/tconnectsync/sync/tandemsource/process_alarm.py index 14945e3..de602c0 100644 --- a/tconnectsync/sync/tandemsource/process_alarm.py +++ b/tconnectsync/sync/tandemsource/process_alarm.py @@ -63,11 +63,11 @@ class ProcessAlarm: return NightscoutEntry.alarm( created_at = event.eventTimestamp.format(), reason = "%s" % event.alarmid.name, - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) elif type(event) == eventtypes.LidMalfunctionActivated: return NightscoutEntry.alarm( created_at = event.eventTimestamp.format(), reason = "Malfunction", - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_basal.py b/tconnectsync/sync/tandemsource/process_basal.py index b8135ef..6da3e0f 100644 --- a/tconnectsync/sync/tandemsource/process_basal.py +++ b/tconnectsync/sync/tandemsource/process_basal.py @@ -85,7 +85,7 @@ class ProcessBasal: duration_mins = duration.seconds / 60, created_at = start.format(), reason = ', '.join(bitmask_to_list(event.changetype)), - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) if type(event) == eventtypes.LidBasalDelivery: value = insulin_milliunits_to_real(event.commandedRate) @@ -97,5 +97,5 @@ class ProcessBasal: duration_mins = duration.seconds / 60, created_at = start.format(), reason = ', '.join(bitmask_to_list(event.commandedRateSource)), - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_basal_resume.py b/tconnectsync/sync/tandemsource/process_basal_resume.py index 48b2af4..edc0205 100644 --- a/tconnectsync/sync/tandemsource/process_basal_resume.py +++ b/tconnectsync/sync/tandemsource/process_basal_resume.py @@ -62,5 +62,5 @@ class ProcessBasalResume: if type(event) == eventtypes.LidPumpingResumed: return NightscoutEntry.basalresume( created_at = event.eventTimestamp.format(), - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_basal_suspension.py b/tconnectsync/sync/tandemsource/process_basal_suspension.py index e32dd6f..6ea8df2 100644 --- a/tconnectsync/sync/tandemsource/process_basal_suspension.py +++ b/tconnectsync/sync/tandemsource/process_basal_suspension.py @@ -63,5 +63,5 @@ class ProcessBasalSuspension: return NightscoutEntry.basalsuspension( created_at = event.eventTimestamp.format(), reason = ', '.join(bitmask_to_list(event.suspendreason)), - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_bolus.py b/tconnectsync/sync/tandemsource/process_bolus.py index 4fdd28d..3c80f74 100644 --- a/tconnectsync/sync/tandemsource/process_bolus.py +++ b/tconnectsync/sync/tandemsource/process_bolus.py @@ -46,7 +46,7 @@ class ProcessBolus: if type(event) == eventtypes.LidBolusCompleted: if last_upload_time and arrow.get(event.eventTimestamp) <= last_upload_time: if self.pretend: - logger.info("Skipping bolusCompletedEvent before last upload time: %s (time range: %s - %s)" % (event, time_start, time_end)) + logger.info("Skipping bolusCompletedEvent not after last upload time: %s (time range: %s - %s)" % (event, time_start, time_end)) continue bolusCompletedEvents.append(event) @@ -91,10 +91,10 @@ class ProcessBolus: suffix = (' ' + (' '.join(suffixes))) if suffixes else '' - event_ids = [] + seq_nums = [] for e in [bolusCompleted, bolusRequested1, bolusRequested2, bolusRequested3]: if e: - event_ids.append(str(e.eventId)) + seq_nums.append(str(e.seqNum)) notes = '' if bolusRequested2 and str(bolusRequested2.optionsRaw) in eventtypes.LidBolusRequestedMsg2.OptionsMap: @@ -107,6 +107,6 @@ class ProcessBolus: created_at = bolusCompleted.eventTimestamp.format(), notes = notes + suffix, bg = bolusRequested1.BG if bolusRequested1 and bolusRequested1.BG > 0 else None, - pump_event_id = ",".join(event_ids) + pump_event_id = ",".join(seq_nums) ) diff --git a/tconnectsync/sync/tandemsource/process_cartridge.py b/tconnectsync/sync/tandemsource/process_cartridge.py index d8b880c..de53db9 100644 --- a/tconnectsync/sync/tandemsource/process_cartridge.py +++ b/tconnectsync/sync/tandemsource/process_cartridge.py @@ -82,19 +82,19 @@ class ProcessCartridge: return NightscoutEntry.sitechange( created_at = cartFilled.eventTimestamp.format(), reason = "Cartridge Filled" + (" (%du filled)" % round(cartFilled.v2Volume) if cartFilled.v2Volume else ""), - pump_event_id = "%s" % cartFilled.eventId + pump_event_id = "%s" % cartFilled.seqNum ) def cannula_to_nsentry(self, cannulaFilled): return NightscoutEntry.sitechange( created_at = cannulaFilled.eventTimestamp.format(), reason = "Cannula Filled" + (" (%du primed)" % round(cannulaFilled.primesize, 2) if cannulaFilled.primesize else ""), - pump_event_id = "%s" % cannulaFilled.eventId + pump_event_id = "%s" % cannulaFilled.seqNum ) def tubing_to_nsentry(self, tubingFilled): return NightscoutEntry.sitechange( created_at = tubingFilled.eventTimestamp.format(), reason = "Tubing Filled" + (" (%du primed)" % round(tubingFilled.primesize) if tubingFilled.primesize else ""), - pump_event_id = "%s" % tubingFilled.eventId + pump_event_id = "%s" % tubingFilled.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_cgm_alert.py b/tconnectsync/sync/tandemsource/process_cgm_alert.py index c29e3b6..ccb3c63 100644 --- a/tconnectsync/sync/tandemsource/process_cgm_alert.py +++ b/tconnectsync/sync/tandemsource/process_cgm_alert.py @@ -73,7 +73,7 @@ class ProcessCGMAlert: return NightscoutEntry.cgm_alert( created_at = alert.eventTimestamp.format(), reason = ("CGM Alert (%s)" % alert.dalertid.name) if alert.dalertid else "CGM Alert (Unknown)", - pump_event_id = "%s" % alert.eventId + pump_event_id = "%s" % alert.seqNum ) elif type(alert) == eventtypes.LidCgmAlertActivatedDex: if alert.dalertid == eventtypes.LidCgmAlertActivatedDex.DalertidEnum.CgmOutOfRange: @@ -82,11 +82,11 @@ class ProcessCGMAlert: return NightscoutEntry.cgm_alert( created_at = alert.eventTimestamp.format(), reason = ("Dexcom CGM Alert (%s)" % alert.dalertid.name) if alert.dalertid else "Dexcom CGM Alert (Unknown)", - pump_event_id = "%s" % alert.eventId + pump_event_id = "%s" % alert.seqNum ) elif type(alert) == eventtypes.LidCgmAlertActivatedFsl2: return NightscoutEntry.cgm_alert( created_at = alert.eventTimestamp.format(), reason = ("Libre CGM Alert (%s)" % alert.dalertid.name) if alert.dalertid else "Libre CGM Alert (Unknown)", - pump_event_id = "%s" % alert.eventId + pump_event_id = "%s" % alert.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_cgm_reading.py b/tconnectsync/sync/tandemsource/process_cgm_reading.py index b42aafe..6c7afe1 100644 --- a/tconnectsync/sync/tandemsource/process_cgm_reading.py +++ b/tconnectsync/sync/tandemsource/process_cgm_reading.py @@ -72,5 +72,5 @@ class ProcessCGMReading: return NightscoutEntry.entry( sgv = event.currentglucosedisplayvalue, created_at = self.timestamp_for(event).format(), - pump_event_id = "%s" % event.eventId, + pump_event_id = "%s" % event.seqNum, ) diff --git a/tconnectsync/sync/tandemsource/process_cgm_start_join_stop.py b/tconnectsync/sync/tandemsource/process_cgm_start_join_stop.py index 9b7f1a8..ea1bae6 100644 --- a/tconnectsync/sync/tandemsource/process_cgm_start_join_stop.py +++ b/tconnectsync/sync/tandemsource/process_cgm_start_join_stop.py @@ -80,17 +80,17 @@ class ProcessCGMStartJoinStop: return NightscoutEntry.cgm_start( created_at = event.eventTimestamp.format(), reason = "CGM Session Started", - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) elif type(event) in EventClass._CGM_JOIN: return NightscoutEntry.cgm_join( created_at = event.eventTimestamp.format(), reason = "CGM Session Joined", - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) elif type(event) in EventClass._CGM_STOP: return NightscoutEntry.cgm_stop( created_at = event.eventTimestamp.format(), reason = "CGM Session Stopped", - pump_event_id = "%s" % event.eventId + pump_event_id = "%s" % event.seqNum ) diff --git a/tconnectsync/sync/tandemsource/process_device_status.py b/tconnectsync/sync/tandemsource/process_device_status.py new file mode 100644 index 0000000..9a4acb3 --- /dev/null +++ b/tconnectsync/sync/tandemsource/process_device_status.py @@ -0,0 +1,76 @@ +import logging +import arrow + +from ...features import DEFAULT_FEATURES +from ... import features +from ...eventparser.generic import Events, decode_raw_events, EVENT_LEN +from ...eventparser.utils import bitmask_to_list +from ...eventparser import events as eventtypes +from ...domain.tandemsource.event_class import EventClass +from ...parser.nightscout import ( + EXERCISE_EVENTTYPE, + SLEEP_EVENTTYPE, + NightscoutEntry +) + +logger = logging.getLogger(__name__) + +class ProcessDeviceStatus: + def __init__(self, tconnect, nightscout, tconnect_device_id, pretend, features=DEFAULT_FEATURES): + self.tconnect = tconnect + self.nightscout = nightscout + self.tconnect_device_id = tconnect_device_id + self.pretend = pretend + self.features = features + + def enabled(self): + return features.PUMP_EVENTS in self.features + + def process(self, events, time_start, time_end): + logger.debug("ProcessDeviceStatus: querying for last uploaded devicestatus") + last_upload = self.nightscout.last_uploaded_devicestatus(time_start=time_start, time_end=time_end) + last_upload_time = None + if last_upload: + last_upload_time = arrow.get(last_upload["created_at"]) + logger.info("ProcessDeviceStatus: Last Nightscout devicestatus upload: %s" % last_upload_time) + + + last_daily_basal_event = None + for event in sorted(events, key=lambda x: self.timestamp_for(x)): + if last_upload_time and self.timestamp_for(event) <= last_upload_time: + if self.pretend: + logger.info("ProcessDeviceStatus: Skipping %s not after last upload time: %s (time range: %s - %s)" % (type(event), event, time_start, time_end)) + continue + + if isinstance(event, eventtypes.LidDailyBasal): + last_daily_basal_event = event + + + if not last_daily_basal_event: + logger.info("ProcessDeviceStatus: No last_daily_basal_event found for add (time range: %s - %s)" % (time_start, time_end)) + + + ns_entries = [] + ns_entries.append(self.daily_basal_to_nsentry(last_daily_basal_event)) + return ns_entries + + def daily_basal_to_nsentry(event): + return NightscoutEntry.devicestatus( + created_at=event.eventTimestamp.format(), + batteryVoltage=(float(event.batterylipomillivolts or 0)/1000), + batteryString="%s%s" % (event.batterychargepercent, '%'), + pump_event_id = "%s" % event.seqNum + ) + + + def write(self, ns_entries): + count = 0 + for entry in ns_entries: + if self.pretend: + logger.info("Would upload devicestatus to Nightscout: %s" % entry) + else: + logger.info("Uploading devicestatus to Nightscout: %s" % entry) + self.nightscout.upload_entry(entry, entity='devicestatus') + count += 1 + + return count diff --git a/tconnectsync/sync/tandemsource/process_user_mode.py b/tconnectsync/sync/tandemsource/process_user_mode.py index 28c0e79..8a6200b 100644 --- a/tconnectsync/sync/tandemsource/process_user_mode.py +++ b/tconnectsync/sync/tandemsource/process_user_mode.py @@ -157,7 +157,7 @@ class ProcessUserMode: reason=reason, duration=duration_mins, event_type=SLEEP_EVENTTYPE, - pump_event_id = "%s,%s" % (start.eventId, stop.eventId) + pump_event_id = "%s,%s" % (start.seqNum, stop.seqNum) ) elif start: reason = None @@ -172,7 +172,7 @@ class ProcessUserMode: reason=reason + " - " + NOT_ENDED if reason else NOT_ENDED, duration=duration_mins, event_type=SLEEP_EVENTTYPE, - pump_event_id = "%s" % start.eventId + pump_event_id = "%s" % start.seqNum ) @@ -191,7 +191,7 @@ class ProcessUserMode: reason=reason, duration=duration_mins, event_type=EXERCISE_EVENTTYPE, - pump_event_id = "%s,%s" % (start.eventId, stop.eventId) + pump_event_id = "%s,%s" % (start.seqNum, stop.seqNum) ) elif start: reason = "Exercise" @@ -204,7 +204,7 @@ class ProcessUserMode: reason=reason + " - " + NOT_ENDED, duration=duration_mins, event_type=EXERCISE_EVENTTYPE, - pump_event_id = "%s" % start.eventId + pump_event_id = "%s" % start.seqNum ) def process_unended_sleep_stop(self, event, sleep_last_upload): @@ -220,7 +220,7 @@ class ProcessUserMode: reason=sleep_last_upload["reason"].replace(" - %s" % NOT_ENDED, ""), duration=duration_mins, event_type=SLEEP_EVENTTYPE, - pump_event_id="%s,%s" % (sleep_last_upload.get("pump_event_id",""), event.eventId) + pump_event_id="%s,%s" % (sleep_last_upload.get("pump_event_id",""), event.seqNum) ) def process_unended_exercise_stop(self, event, exercise_last_upload): @@ -240,5 +240,5 @@ class ProcessUserMode: reason=reason, duration=duration_mins, event_type=EXERCISE_EVENTTYPE, - pump_event_id="%s,%s" % (exercise_last_upload.get("pump_event_id",""), event.eventId) + pump_event_id="%s,%s" % (exercise_last_upload.get("pump_event_id",""), event.seqNum) ) \ No newline at end of file