mirror of
https://github.com/bckelley/tconnectsync.git
synced 2026-08-28 12:11:50 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d58b23d27 | ||
|
|
40aea810cc | ||
|
|
ec84afdb7a | ||
|
|
fca7f573a3 | ||
|
|
76fc043a2b | ||
|
|
86e07880a9 | ||
|
|
751087373c | ||
|
|
f47421e482 | ||
|
|
dfc39d61bb |
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: 'Setup help: tconnectsync-heroku'
|
||||
about: When experiencing an issue setting up tconnectsync-heroku
|
||||
title: ''
|
||||
labels: heroku, setup help
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the problem**
|
||||
A clear and concise description of the issue you're experiencing.
|
||||
|
||||
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
|
||||
|
||||
**Have you followed the [Troubleshooting steps in the README?](https://github.com/jwoglom/tconnectsync-heroku#troubleshooting)** (Yes or No)
|
||||
|
||||
|
||||
|
||||
**Setup details**
|
||||
|
||||
* **On what platform are you using the t:connect mobile app?** (Android or iOS)
|
||||
* **What version are you using of the t:connect mobile app?**
|
||||
|
||||
|
||||
|
||||
**Heroku log output**
|
||||
If applicable, add the full output from the heroku logs in More > View Logs. ([See the instructions in the README.](https://github.com/jwoglom/tconnectsync-heroku#testing))
|
||||
|
||||
|
||||
|
||||
**Check Login output**
|
||||
If applicable, [please follow the instructions to visit the `check_login` page](https://github.com/jwoglom/tconnectsync-heroku#testing), and copy and paste the output here.
|
||||
|
||||
This file may contain sensitive details like your Nightscout URL and pump serial number. Copy the output into a text editor like Textedit or Notepad first and find-and-replace sensitive strings, like your Nightscout URL and pump serial number, if they appear.
|
||||
|
||||
The output will also contain pump and CGM related information such as delivery events and blood sugars. If you would prefer to keep the contents private, send an email to tconnectsync<at>wogloms.net with the log output so @jwoglom can investigate.
|
||||
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: 'Setup help: tconnectsync'
|
||||
about: When experiencing an issue setting up tconnectsync on Windows, Linux, or MacOS.
|
||||
title: ''
|
||||
labels: setup help
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the problem**
|
||||
A clear and concise description of the issue you're experiencing.
|
||||
|
||||
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
|
||||
|
||||
**Setup details**
|
||||
|
||||
* **Operating system:**
|
||||
* **tconnectsync version** (from `--version`):
|
||||
* **On what platform are you using the t:connect mobile app?** (Android or iOS)
|
||||
* **What version are you using of the t:connect mobile app?**
|
||||
|
||||
|
||||
|
||||
**Terminal output**
|
||||
If applicable, add the full output from your terminal from running tconnectsync. Copy the output into a text editor like Textedit or Notepad first and find-and-replace sensitive strings, like your Nightscout URL and pump serial number, if they appear.
|
||||
|
||||
|
||||
|
||||
**Check Login output**
|
||||
If applicable, please run `--check-login` and upload the tconnectsync-check-output.log file as an attachment on the issue. This file should have sensitive details like your Nightscout URL and pump serial number automatically removed, but double-check before uploading.
|
||||
|
||||
This log will contain pump and CGM related information such as delivery events and blood sugars. If you would prefer to keep the contents private, send an email to tconnectsync<at>wogloms.net with the log output so @jwoglom can investigate.
|
||||
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -116,7 +116,10 @@ First, ensure that you have **Python 3** with **Pip** installed:
|
||||
|
||||
* **On MacOS:** Open Terminal. Install [Homebrew](https://brew.sh/), and then run `brew install python3`
|
||||
* **On Linux:** Follow your distribution's specific instructions.
|
||||
For Debian/Ubuntu based distros, `sudo apt install python3 python3-pip`
|
||||
- For Debian/Ubuntu based distros, `sudo apt install python3 python3-pip`
|
||||
- For CentOS/Rocky Linux 8:
|
||||
- `sudo dnf install python39-pip`
|
||||
- `sudo alternatives --set python /usr/bin/python3.9`
|
||||
* **On Windows:** Install Ubuntu under the [Windows Subsystem for Linux](https://ubuntu.com/wsl).
|
||||
Open the Ubuntu Terminal, then run `sudo apt install python3 python3-pip`.
|
||||
Perform the remainder of the steps under the Ubuntu environment.
|
||||
@@ -126,6 +129,12 @@ Now install the `tconnectsync` package with pip:
|
||||
```
|
||||
$ pip3 install tconnectsync
|
||||
```
|
||||
To install into a user environment instead of system-wide for a more contained install:
|
||||
````
|
||||
$ pip3 install --user tconnectsync
|
||||
````
|
||||
- This will place the tconnectsync binary file at ``/home/<username>/.local/bin/tconnectsync``
|
||||
|
||||
|
||||
If the pip3 command is not found, run `python3 -m pip install tconnectsync` instead.
|
||||
|
||||
@@ -377,13 +386,18 @@ invoking tconnectsync with no arguments via cron.
|
||||
|
||||
If using Pipenv or a virtualenv, make sure that you either prefix the call to main.py with `pipenv run` or source the `bin/activate` file within the virtualenv, so that the proper dependencies are loaded. If not using any kind of virtualenv, you can instead just install the necessary dependencies as specified inside Pipfile globally.
|
||||
|
||||
An example configuration in `/etc/crontab` which runs every 15 minutes:
|
||||
An system-wide example configuration in `/etc/crontab` which runs every 15 minutes, on the 15 minute mark:
|
||||
|
||||
```bash
|
||||
# m h dom mon dow user command
|
||||
0,15,30,45 * * * * root /path/to/tconnectsync/run.sh
|
||||
```
|
||||
|
||||
An example of a user crontab `crontab -e` if not running system-wide, which runs every 15 minutes:
|
||||
```
|
||||
*/15 * * * * /path/to/tconnectsync/run.sh
|
||||
```
|
||||
|
||||
You can use one of the same `run.sh` files referenced above, but remove the `--auto-update` flag since you are handling the functionality for running the script periodically yourself.
|
||||
|
||||
## Tandem APIs
|
||||
@@ -417,4 +431,4 @@ tconnectsync.util.cli.enable_logging()
|
||||
api = tconnectsync.util.cli.get_api()
|
||||
# Make API calls, e.g.
|
||||
therapy_timeline = api.controliq.therapy_timeline('2022-08-01', '2022-08-10')
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 50%
|
||||
threshold: null
|
||||
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = tconnectsync
|
||||
version = 0.8.2
|
||||
version = 0.8.4
|
||||
author = James Woglom
|
||||
author_email = j@wogloms.net
|
||||
description = Syncs Tandem t:connect pump data to Nightscout for the t:slim X2
|
||||
|
||||
@@ -21,8 +21,8 @@ class WS2Api:
|
||||
self.userGuid = userGuid
|
||||
self.session = base_session()
|
||||
|
||||
def get(self, endpoint):
|
||||
r = self.session.get(self.BASE_URL + endpoint, headers=base_headers())
|
||||
def get(self, endpoint, **kwargs):
|
||||
r = self.session.get(self.BASE_URL + endpoint, headers=base_headers(), **kwargs)
|
||||
if r.status_code != 200:
|
||||
raise ApiException(r.status_code, "WS2 API HTTP %s response: %s" % (str(r.status_code), r.text))
|
||||
return r.text
|
||||
|
||||
@@ -51,12 +51,52 @@ class CGMTherapyEvent(TherapyEvent):
|
||||
},
|
||||
"""
|
||||
@classmethod
|
||||
def parse(self, json):
|
||||
def parse(_, json):
|
||||
self = CGMTherapyEvent()
|
||||
TherapyEvent.parse(self, json)
|
||||
self.eventID = json['eventID']
|
||||
self.egv = json['egv']['estimatedGlucoseValue']
|
||||
return self
|
||||
|
||||
class BGTherapyEvent(TherapyEvent):
|
||||
eventID = None
|
||||
egv = None
|
||||
"""
|
||||
{
|
||||
'bg': 160, # note in EGV
|
||||
'cgmCalibration': 1, # not in EGV
|
||||
'description': 'BG',
|
||||
'deviceType': 't:slim X2 Insulin Pump',
|
||||
'eventDateTime': '2022-08-20T07:25:24',
|
||||
'eventTypeId': 16,
|
||||
'indexId': 844955,
|
||||
'interactive': 0,
|
||||
'iob': 0.75,
|
||||
'note': { 'active': False,
|
||||
'eventId': 0, # different location than EGV
|
||||
'eventTypeId': 16,
|
||||
'id': 0,
|
||||
'indexId': '',
|
||||
'sourceRecordId': 0},
|
||||
'requestDateTime': '0001-01-01T00:00:00',
|
||||
'serialNumber': 'xxx',
|
||||
'sourceRecId': 793549667,
|
||||
'tempRateActivated': 0,
|
||||
'tempRateCompleted': 0,
|
||||
'tempRateId': 0,
|
||||
'type': 'BG',
|
||||
'uploadId': 748700213}
|
||||
"""
|
||||
@classmethod
|
||||
def parse(_, json):
|
||||
self = BGTherapyEvent()
|
||||
TherapyEvent.parse(self, json)
|
||||
self.eventID = json['note']['eventId']
|
||||
# This is probably not how we want to provide CGM calibrations to Nightscout,
|
||||
# but will just include it as egv data for now to keep the thing from crashing :)
|
||||
self.egv = json['bg']
|
||||
return self
|
||||
|
||||
class BolusTherapyEvent(TherapyEvent):
|
||||
bolusRequestOptions = None
|
||||
REQUEST_AUTOMATIC = "Automatic Bolus/Correction"
|
||||
@@ -364,4 +404,28 @@ class BolusTherapyEvent(TherapyEvent):
|
||||
"tempRateCompleted": 0,
|
||||
"tempRateActivated": 0
|
||||
}
|
||||
"""
|
||||
CGM Calibration (Therapy Event Type BG):
|
||||
{ 'bg': 160,
|
||||
'cgmCalibration': 1,
|
||||
'description': 'BG',
|
||||
'deviceType': 't:slim X2 Insulin Pump',
|
||||
'eventDateTime': '2022-08-20T07:25:24',
|
||||
'eventTypeId': 16,
|
||||
'indexId': 844955,
|
||||
'interactive': 0,
|
||||
'iob': 0.75,
|
||||
'note': { 'active': False,
|
||||
'eventId': 0,
|
||||
'eventTypeId': 16,
|
||||
'id': 0,
|
||||
'indexId': '',
|
||||
'sourceRecordId': 0},
|
||||
'requestDateTime': '0001-01-01T00:00:00',
|
||||
'serialNumber': 'xxx',
|
||||
'sourceRecId': 793549667,
|
||||
'tempRateActivated': 0,
|
||||
'tempRateCompleted': 0,
|
||||
'tempRateId': 0,
|
||||
'type': 'BG',
|
||||
'uploadId': 0}
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
class Time:
|
||||
def __init__(self, hour: int, min: int):
|
||||
self.hour = hour
|
||||
self.min = min
|
||||
|
||||
@classmethod
|
||||
def parse(cls, input):
|
||||
if ' ' not in input:
|
||||
raise ValueError('unable to parse time: %s' % input)
|
||||
|
||||
hrmin, ampm = input.split(' ')
|
||||
|
||||
hr, min = hrmin.split(':')
|
||||
hr = int(hr)
|
||||
min = int(min)
|
||||
|
||||
if ampm.lower() == 'pm':
|
||||
hr += 12
|
||||
elif ampm.lower() != 'am':
|
||||
raise ValueError('unable to parse time: %s' % input)
|
||||
|
||||
return cls(hr, min)
|
||||
@@ -11,7 +11,7 @@ from .api.common import ApiException
|
||||
from .parser.nightscout import ENTERED_BY
|
||||
|
||||
def format_datetime(date):
|
||||
return arrow.get(date).strftime('%Y-%m-%d %H:%M:%S')
|
||||
return arrow.get(date).isoformat()
|
||||
|
||||
def time_range(field_name, start_time, end_time):
|
||||
arg = ''
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from tconnectsync.domain.therapy_event import BolusTherapyEvent, CGMTherapyEvent
|
||||
from tconnectsync.domain.therapy_event import BolusTherapyEvent, CGMTherapyEvent, BGTherapyEvent
|
||||
from tconnectsync.parser.tconnect import TConnectEntry
|
||||
|
||||
import logging
|
||||
@@ -8,12 +8,16 @@ logger = logging.getLogger(__name__)
|
||||
def split_therapy_events(ciqTherapyEvents):
|
||||
bolusEvents = []
|
||||
cgmEvents = []
|
||||
bgEvents = []
|
||||
for e in ciqTherapyEvents['event']:
|
||||
event = TConnectEntry.parse_therapy_event(e)
|
||||
if type(event) == BolusTherapyEvent:
|
||||
if isinstance(event, BolusTherapyEvent):
|
||||
bolusEvents.append(event)
|
||||
elif type(event) == CGMTherapyEvent:
|
||||
elif isinstance(event, CGMTherapyEvent):
|
||||
cgmEvents.append(event)
|
||||
elif isinstance(event, BGTherapyEvent):
|
||||
bgEvents.append(event)
|
||||
|
||||
logger.debug("split_therapy_events: %d bolus, %d CGM" % (len(bolusEvents), len(cgmEvents)))
|
||||
logger.debug("split_therapy_events: %d bolus, %d CGM, %d BG" % (len(bolusEvents), len(cgmEvents), len(bgEvents)))
|
||||
# TODO: BG events (CGM Calibration) values are not currently returned from ciq_therapy_events.py
|
||||
return bolusEvents, cgmEvents
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
import arrow
|
||||
from tconnectsync.domain.bolus import Bolus
|
||||
|
||||
from tconnectsync.domain.therapy_event import BolusTherapyEvent, CGMTherapyEvent
|
||||
from tconnectsync.domain.therapy_event import BolusTherapyEvent, CGMTherapyEvent, BGTherapyEvent
|
||||
|
||||
try:
|
||||
from ..secret import TIMEZONE_NAME
|
||||
@@ -200,6 +200,8 @@ class TConnectEntry:
|
||||
return BolusTherapyEvent.parse(data)
|
||||
elif data["type"] == "CGM":
|
||||
return CGMTherapyEvent.parse(data)
|
||||
elif data["type"] == "BG":
|
||||
return BGTherapyEvent.parse(data)
|
||||
|
||||
raise UnknownTherapyEventException(data)
|
||||
|
||||
@@ -213,4 +215,5 @@ class UnknownBasalSuspensionEventException(Exception):
|
||||
|
||||
class UnknownTherapyEventException(Exception):
|
||||
def __init__(self, data):
|
||||
super().__init__("Unknown therapy event type: " % data)
|
||||
typ = data["type"]
|
||||
super().__init__(f"Unknown therapy event type: {typ} in {data}")
|
||||
|
||||
Reference in New Issue
Block a user