Skip to content

Commit ce19874

Browse files
committed
Timeseries functions now default to returning all data: ID and start/end args optional
1 parent 19d8b16 commit ce19874

23 files changed

+718
-603
lines changed

caar.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ caar.cleanthermostat module
1010
.. automodule:: caar.cleanthermostat
1111
:members:
1212
:no-undoc-members:
13-
:exclude-members: Cycle, Inside, Outside
13+
:exclude-members: Cycle, Sensors, Geospatial
1414
:show-inheritance:
1515

1616
caar.history module
@@ -19,7 +19,7 @@ caar.history module
1919
.. automodule:: caar.history
2020
:members:
2121
:no-undoc-members:
22-
:exclude-members: Cycle, Inside, Outside
22+
:exclude-members: Cycle, Sensors, Geospatial
2323
:show-inheritance:
2424

2525
caar.histsummary module
@@ -28,7 +28,7 @@ caar.histsummary module
2828
.. automodule:: caar.histsummary
2929
:members:
3030
:no-undoc-members:
31-
:exclude-members: squared_avg_daily_data_points_per_id, matching_ids_all_dfs, number_of_days, start_of_first_full_day_df, date_range_for_data, count_inside_temp_by_thermo_id, count_inside_temps_in_intervals_for_thermo_id, counts_by_primary_id_squared, dt_timedelta_from_frequency
31+
:exclude-members: squared_avg_daily_data_points_per_id, matching_ids_all_dfs, number_of_days, start_of_first_full_day_df, number_of_intervals_in_date_range, count_observations_by_sensor_id, count_observations_in_intervals_for_sensor_id, counts_by_primary_id_squared, dt_timedelta_from_frequency
3232
:show-inheritance:
3333

3434
caar.timeseries module

caar/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from caar.cleanthermostat import pickle_from_file
44

55
from caar.history import create_cycles_df
6-
from caar.history import create_inside_df
7-
from caar.history import create_outside_df
6+
from caar.history import create_sensors_df
7+
from caar.history import create_geospatial_df
88
from caar.history import random_record
99

1010
from caar.histsummary import days_of_data_by_id
@@ -15,10 +15,10 @@
1515
from caar.histsummary import df_select_datetime_range
1616
from caar.histsummary import count_of_data_points_for_each_id
1717
from caar.histsummary import count_of_data_points_for_select_id
18-
from caar.histsummary import location_id_of_thermo
18+
from caar.histsummary import location_id_of_sensor
1919

20-
from caar.timeseries import time_series_cycling_and_temps
20+
from caar.timeseries import cycling_and_obs_arrays
2121
from caar.timeseries import on_off_status
22-
from caar.timeseries import temps_arr_by_freq
22+
from caar.timeseries import sensor_obs_arr_by_freq
2323
from caar.timeseries import plot_cycles_xy
24-
from caar.timeseries import plot_temps_xy
24+
from caar.timeseries import plot_sensor_geo_xy

caar/cleanthermostat.py

Lines changed: 174 additions & 171 deletions
Large diffs are not rendered by default.

caar/config.ini

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,35 @@ TEST_POSTAL_FILE = test_us_postal_codes_clean.csv
1313

1414
[test_pickle_files] # For Python 3.4, 3.5
1515
CYCLES_PICKLE_FILE_OUT = TX_cycles.pickle
16-
INSIDE_PICKLE_FILE_OUT = TX_inside.pickle
17-
OUTSIDE_PICKLE_FILE_OUT = TX_outside.pickle
16+
SENSOR_PICKLE_FILE_OUT = TX_sensors.pickle
17+
GEOSPATIAL_PICKLE_FILE_OUT = TX_geospatial.pickle
1818
CYCLES_PICKLE_FILE = test_TX_cycles.pickle
19-
INSIDE_PICKLE_FILE = test_TX_inside.pickle
20-
OUTSIDE_PICKLE_FILE = test_TX_outside.pickle
19+
SENSOR_PICKLE_FILE = test_TX_sensors.pickle
20+
GEOSPATIAL_PICKLE_FILE = test_TX_geospatial.pickle
2121
ALL_STATES_CYCLES_PICKLED_OUT = all_states_cycles.pickle
22-
ALL_STATES_INSIDE_PICKLED_OUT = all_states_inside.pickle
23-
ALL_STATES_OUTSIDE_PICKLED_OUT = all_states_outside.pickle
22+
ALL_STATES_INSIDE_PICKLED_OUT = all_states_sensors.pickle
23+
ALL_STATES_OUTSIDE_PICKLED_OUT = all_states_geospatial.pickle
2424
ALL_STATES_CYCLES_PICKLED = test_all_states_cycles.pickle
25-
ALL_STATES_INSIDE_PICKLED = test_all_states_inside.pickle
26-
ALL_STATES_OUTSIDE_PICKLED= test_all_states_outside.pickle
25+
ALL_STATES_INSIDE_PICKLED = test_all_states_sensors.pickle
26+
ALL_STATES_OUTSIDE_PICKLED = test_all_states_geospatial.pickle
2727

2828
[test_pickle_files_py2] # For Python 2.7
2929
CYCLES_PICKLE_FILE_OUT = TX_cycles_py27.pickle
30-
INSIDE_PICKLE_FILE_OUT = TX_inside_py27.pickle
31-
OUTSIDE_PICKLE_FILE_OUT = TX_outside_py27.pickle
30+
SENSOR_PICKLE_FILE_OUT = TX_sensors_py27.pickle
31+
GEOSPATIAL_PICKLE_FILE_OUT = TX_geospatial_py27.pickle
3232
CYCLES_PICKLE_FILE = test_TX_cycles_py27.pickle
33-
INSIDE_PICKLE_FILE = test_TX_inside_py27.pickle
34-
OUTSIDE_PICKLE_FILE = test_TX_outside_py27.pickle
33+
SENSOR_PICKLE_FILE = test_TX_sensors_py27.pickle
34+
GEOSPATIAL_PICKLE_FILE = test_TX_geospatial_py27.pickle
3535
ALL_STATES_CYCLES_PICKLED_OUT = all_states_cycles_py27.pickle
36-
ALL_STATES_INSIDE_PICKLED_OUT = all_states_inside_py27.pickle
37-
ALL_STATES_OUTSIDE_PICKLED_OUT = all_states_outside_py27.pickle
36+
ALL_STATES_INSIDE_PICKLED_OUT = all_states_sensors_py27.pickle
37+
ALL_STATES_OUTSIDE_PICKLED_OUT = all_states_geospatial_py27.pickle
3838
ALL_STATES_CYCLES_PICKLED = test_all_states_cycles_py27.pickle
39-
ALL_STATES_INSIDE_PICKLED = test_all_states_inside_py27.pickle
40-
ALL_STATES_OUTSIDE_PICKLED= test_all_states_outside_py27.pickle
39+
ALL_STATES_INSIDE_PICKLED = test_all_states_sensors_py27.pickle
40+
ALL_STATES_OUTSIDE_PICKLED = test_all_states_geospatial_py27.pickle
4141

4242
[test_ids_and_states]
43-
THERMO_ID1 = 92
44-
THERMO_ID2 = 124
43+
SENSOR_ID1 = 92
44+
SENSOR_ID2 = 124
4545
LOCATION_ID1 = 81
4646
LOCATION_ID2 = 112
4747
STATE = TX
@@ -68,23 +68,23 @@ CYCLE_END_TIME_INDEX = 3
6868
INSIDE_FIELD1 = ThermostatId
6969
INSIDE_FIELD2 = LogDate
7070
INSIDE_FIELD3 = Degrees
71-
INSIDE_ID_INDEX = 0
72-
INSIDE_LOG_DATE_INDEX = 1
73-
INSIDE_DEGREES_INDEX = 2
71+
SENSOR_ID_INDEX = 0
72+
SENSORS_LOG_DATE_INDEX = 1
73+
SENSORS_DATA_INDEX = 2
7474

7575
OUTSIDE_FIELD1 = LocationId
7676
OUTSIDE_FIELD2 = LogDate
7777
OUTSIDE_FIELD3 = Degrees
7878
OUTSIDE_TIMESTAMP_LABEL = LogDate
7979
OUTSIDE_DEGREES_LABEL = Degrees
80-
OUTSIDE_ID_INDEX = 0
81-
UNIQUE_OUTSIDE_FIELD_INDEX = 0
82-
OUTSIDE_LOG_DATE_INDEX = 1
83-
OUTSIDE_DEGREES_INDEX = 2
80+
GEOSPATIAL_ID_INDEX = 0
81+
UNIQUE_GEOSPATIAL_FIELD_INDEX = 0
82+
GEOSPATIAL_LOG_DATE_INDEX = 1
83+
GEOSPATIAL_OBSERVATION_INDEX = 2
8484

85-
THERMOSTAT_DEVICE_ID = Id
86-
THERMOSTAT_LOCATION_ID = LocationId
87-
THERMOSTAT_ZIP_CODE = ZipCode
85+
SENSOR_DEVICE_ID = Id
86+
SENSOR_LOCATION_ID = LocationId
87+
SENSOR_ZIP_CODE = ZipCode
8888

8989
POSTAL_FILE_ZIP = Postal Code
9090
POSTAL_TWO_LETTER_STATE = State Abbreviation

caar/configparser_read.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,40 +48,40 @@
4848
INSIDE_FIELD1 = parser.get('file_headers', 'INSIDE_FIELD1')
4949
INSIDE_FIELD2 = parser.get('file_headers', 'INSIDE_FIELD2')
5050
INSIDE_FIELD3 = parser.get('file_headers', 'INSIDE_FIELD3')
51-
INSIDE_FIELDS = tuple([INSIDE_FIELD1, INSIDE_FIELD2, INSIDE_FIELD3])
51+
SENSOR_FIELDS = tuple([INSIDE_FIELD1, INSIDE_FIELD2, INSIDE_FIELD3])
5252

53-
# THERMO_ID_FIELD is the string heading of corresponding field
54-
# INSIDE_ID_INDEX gives the index of the INSIDE field containing device ID
55-
# in the tuple INSIDE_FIELDS.
56-
THERMO_ID_FIELD = INSIDE_FIELDS[int(parser.get('file_headers', 'INSIDE_ID_INDEX'))]
53+
# SENSOR_ID_FIELD is the string heading of corresponding field
54+
# SENSOR_ID_INDEX gives the index of the INSIDE field containing device ID
55+
# in the tuple SENSOR_FIELDS.
56+
SENSOR_ID_FIELD = SENSOR_FIELDS[int(parser.get('file_headers', 'SENSOR_ID_INDEX'))]
5757

5858
# Ints: 0-based positions of fields in raw file
59-
INSIDE_ID_INDEX = int(parser.get('file_headers', 'INSIDE_ID_INDEX'))
60-
INSIDE_LOG_DATE_INDEX = int(parser.get('file_headers', 'INSIDE_LOG_DATE_INDEX'))
61-
INSIDE_DEGREES_INDEX = int(parser.get('file_headers', 'INSIDE_DEGREES_INDEX'))
59+
SENSOR_ID_INDEX = int(parser.get('file_headers', 'SENSOR_ID_INDEX'))
60+
SENSORS_LOG_DATE_INDEX = int(parser.get('file_headers', 'SENSORS_LOG_DATE_INDEX'))
61+
SENSORS_DATA_INDEX = int(parser.get('file_headers', 'SENSORS_DATA_INDEX'))
6262
# INSIDE_TEMP_FIELD is the string heading of corresponding field
6363
# INSIDE_TEMP_INDEX is index of field containing inside temperature
64-
INSIDE_TEMP_FIELD = INSIDE_FIELDS[int(parser.get('file_headers', 'INSIDE_DEGREES_INDEX'))]
64+
INSIDE_TEMP_FIELD = SENSOR_FIELDS[int(parser.get('file_headers', 'SENSORS_DATA_INDEX'))]
6565

6666
# Outside observation file column names
6767
OUTSIDE_FIELD1 = parser.get('file_headers', 'OUTSIDE_FIELD1')
6868
OUTSIDE_FIELD2 = parser.get('file_headers', 'OUTSIDE_FIELD2')
6969
OUTSIDE_FIELD3 = parser.get('file_headers', 'OUTSIDE_FIELD3')
70-
OUTSIDE_FIELDS = tuple([OUTSIDE_FIELD1, OUTSIDE_FIELD2, OUTSIDE_FIELD3])
70+
GEOSPATIAL_FIELDS = tuple([OUTSIDE_FIELD1, OUTSIDE_FIELD2, OUTSIDE_FIELD3])
7171

7272
OUTSIDE_TIMESTAMP_LABEL = parser.get('file_headers', 'OUTSIDE_TIMESTAMP_LABEL')
7373
OUTSIDE_DEGREES_LABEL = parser.get('file_headers', 'OUTSIDE_DEGREES_LABEL')
7474
# Column heading that is unique to outside data file
75-
UNIQUE_OUTSIDE_FIELD = OUTSIDE_FIELDS[int(parser.get('file_headers', 'UNIQUE_OUTSIDE_FIELD_INDEX'))]
75+
UNIQUE_GEOSPATIAL_FIELD = GEOSPATIAL_FIELDS[int(parser.get('file_headers', 'UNIQUE_GEOSPATIAL_FIELD_INDEX'))]
7676
# Ints: 0-based positions of fields in raw files
77-
OUTSIDE_ID_INDEX = int(parser.get('file_headers', 'OUTSIDE_ID_INDEX'))
78-
OUTSIDE_LOG_DATE_INDEX = int(parser.get('file_headers', 'OUTSIDE_LOG_DATE_INDEX'))
79-
OUTSIDE_DEGREES_INDEX = int(parser.get('file_headers', 'OUTSIDE_DEGREES_INDEX'))
77+
GEOSPATIAL_ID_INDEX = int(parser.get('file_headers', 'GEOSPATIAL_ID_INDEX'))
78+
GEOSPATIAL_LOG_DATE_INDEX = int(parser.get('file_headers', 'GEOSPATIAL_LOG_DATE_INDEX'))
79+
GEOSPATIAL_OBSERVATION_INDEX = int(parser.get('file_headers', 'GEOSPATIAL_OBSERVATION_INDEX'))
8080

8181
# Thermostat file metadata file column names
82-
THERMOSTAT_DEVICE_ID = parser.get('file_headers', 'THERMOSTAT_DEVICE_ID')
83-
THERMOSTAT_LOCATION_ID = parser.get('file_headers', 'THERMOSTAT_LOCATION_ID')
84-
THERMOSTAT_ZIP_CODE = parser.get('file_headers', 'THERMOSTAT_ZIP_CODE')
82+
SENSOR_DEVICE_ID = parser.get('file_headers', 'SENSOR_DEVICE_ID')
83+
SENSOR_LOCATION_ID = parser.get('file_headers', 'SENSOR_LOCATION_ID')
84+
SENSOR_ZIP_CODE = parser.get('file_headers', 'SENSOR_ZIP_CODE')
8585

8686
# Postal file containing zip codes and other geographic metadata
8787
POSTAL_FILE_ZIP = parser.get('file_headers', 'POSTAL_FILE_ZIP')
@@ -112,9 +112,9 @@
112112
TEST_DIR = parser.get('test_files', 'TEST_DIR')
113113

114114
# Ints
115-
THERMO_ID1 = int(parser.get('test_ids_and_states', 'THERMO_ID1'))
116-
THERMO_ID2 = int(parser.get('test_ids_and_states', 'THERMO_ID2'))
117-
THERMO_IDS = [THERMO_ID1, THERMO_ID2]
115+
SENSOR_ID1 = int(parser.get('test_ids_and_states', 'SENSOR_ID1'))
116+
SENSOR_ID2 = int(parser.get('test_ids_and_states', 'SENSOR_ID2'))
117+
SENSOR_IDS = [SENSOR_ID1, SENSOR_ID2]
118118
LOCATION_ID1 = int(parser.get('test_ids_and_states', 'LOCATION_ID1'))
119119
LOCATION_ID2 = int(parser.get('test_ids_and_states', 'LOCATION_ID2'))
120120
LOCATION_IDS = [LOCATION_ID1, LOCATION_ID2]
@@ -140,21 +140,21 @@
140140
if '2.7' in sys.version:
141141
test_pickle_section += '_py2'
142142

143-
options_vals = ['CYCLES_PICKLE_FILE_OUT', 'INSIDE_PICKLE_FILE_OUT', 'OUTSIDE_PICKLE_FILE_OUT',
144-
'CYCLES_PICKLE_FILE', 'INSIDE_PICKLE_FILE', 'OUTSIDE_PICKLE_FILE',
143+
options_vals = ['CYCLES_PICKLE_FILE_OUT', 'SENSOR_PICKLE_FILE_OUT', 'GEOSPATIAL_PICKLE_FILE_OUT',
144+
'CYCLES_PICKLE_FILE', 'SENSOR_PICKLE_FILE', 'GEOSPATIAL_PICKLE_FILE',
145145
'ALL_STATES_CYCLES_PICKLED_OUT', 'ALL_STATES_INSIDE_PICKLED_OUT', 'ALL_STATES_OUTSIDE_PICKLED_OUT',
146146
'ALL_STATES_CYCLES_PICKLED', 'ALL_STATES_INSIDE_PICKLED', 'ALL_STATES_OUTSIDE_PICKLED']
147147

148148
for option_val in options_vals:
149149
vars()[option_val] = os.path.join(TEST_DIR, parser.get(test_pickle_section, option_val))
150150

151151
CYCLES_PICKLE_FILE_OUT = vars()['CYCLES_PICKLE_FILE_OUT']
152-
INSIDE_PICKLE_FILE_OUT = vars()['INSIDE_PICKLE_FILE_OUT']
153-
OUTSIDE_PICKLE_FILE_OUT = vars()['OUTSIDE_PICKLE_FILE_OUT']
152+
SENSOR_PICKLE_FILE_OUT = vars()['SENSOR_PICKLE_FILE_OUT']
153+
GEOSPATIAL_PICKLE_FILE_OUT = vars()['GEOSPATIAL_PICKLE_FILE_OUT']
154154

155155
CYCLES_PICKLE_FILE = vars()['CYCLES_PICKLE_FILE']
156-
INSIDE_PICKLE_FILE = vars()['INSIDE_PICKLE_FILE']
157-
OUTSIDE_PICKLE_FILE = vars()['OUTSIDE_PICKLE_FILE']
156+
SENSOR_PICKLE_FILE = vars()['SENSOR_PICKLE_FILE']
157+
GEOSPATIAL_PICKLE_FILE = vars()['GEOSPATIAL_PICKLE_FILE']
158158

159159
ALL_STATES_CYCLES_PICKLED_OUT = vars()['ALL_STATES_CYCLES_PICKLED_OUT']
160160
ALL_STATES_INSIDE_PICKLED_OUT = vars()['ALL_STATES_INSIDE_PICKLED_OUT']

caar/history.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,56 +12,55 @@
1212
standard_library.install_aliases()
1313

1414

15-
Cycle = namedtuple('Cycle', ['thermo_id', 'cycle_mode', 'start_time'])
16-
Inside = namedtuple('Inside', ['thermo_id', 'timestamp'])
17-
Outside = namedtuple('Outside', ['location_id', 'timestamp'])
15+
Cycle = namedtuple('Cycle', ['device_id', 'cycle_mode', 'start_time'])
16+
Sensor = namedtuple('Sensor', ['sensor_id', 'timestamp'])
17+
Geospatial = namedtuple('Geospatial', ['location_id', 'timestamp'])
1818

1919

20-
def create_inside_df(dict_or_pickle_file, thermo_ids=None):
21-
"""Returns pandas DataFrame containing thermostat ID, timestamps and
22-
inside temperatures at the time of cooling (or heating) cycles starting
23-
and ending.
20+
def create_sensors_df(dict_or_pickle_file, sensor_ids=None):
21+
"""Returns pandas DataFrame containing sensor ID, timestamps and
22+
sensor observations.
2423
2524
Args:
2625
dict_or_pickle_file (dict or str): The object must have been created with dict_from_file() or pickle_from_file() function.
2726
28-
thermo_ids (Optional[list or other iterable of ints or strings]): Thermostat IDs. If no argument is specified, all IDs from the first arg will be in the DataFrame.
27+
sensor_ids (Optional[list or other iterable of ints or strings]): Sensor IDs. If no argument is specified, all IDs from the first arg will be in the DataFrame.
2928
3029
Returns:
31-
inside_df (pandas DataFrame): DataFrame has MultiIndex based on the
30+
sensors_df (pandas DataFrame): DataFrame has MultiIndex based on the
3231
ID(s) and timestamps.
3332
"""
34-
fields = list(Inside._fields)
33+
fields = list(Sensor._fields)
3534
multi_ids, vals, meta = _records_as_lists_of_tuples(dict_or_pickle_file,
36-
fields, ids=thermo_ids)
35+
fields, ids=sensor_ids)
3736
id_labels = [meta[col]['heading'] for col in ['id', 'time']]
3837
data_labels = _data_labels_from_meta(meta, id_labels)
39-
inside_df = _create_multi_index_df(id_labels, multi_ids, data_labels, vals)
40-
return inside_df
38+
sensors_df = _create_multi_index_df(id_labels, multi_ids, data_labels, vals)
39+
return sensors_df
4140

4241

43-
def create_cycles_df(dict_or_pickle_file, thermo_ids=None):
44-
"""Returns pandas DataFrame containing thermostat ids and cycle beginning
42+
def create_cycles_df(dict_or_pickle_file, device_ids=None):
43+
"""Returns pandas DataFrame containing sensor ids and cycle beginning
4544
timestamps as multi-part indexes, and cycle ending times as values.
4645
4746
Args:
4847
dict_or_pickle_file (dict or str): Must have been created with dict_from_file() or pickle_from_file() function.
4948
50-
thermo_ids (Optional[list or other iterable of ints or strings]): Thermostat IDs. If no argument is specified, all IDs from the first arg will be in the DataFrame.
49+
device_ids (Optional[list or other iterable of ints or strings]): Sensor IDs. If no argument is specified, all IDs from the first arg will be in the DataFrame.
5150
5251
Returns:
5352
cycles_df (pandas DataFrame): DataFrame has MultiIndex based on the ID(s) and timestamps.
5453
"""
5554
multi_ids, vals, meta = _records_as_lists_of_tuples(dict_or_pickle_file,
5655
list(Cycle._fields),
57-
ids=thermo_ids)
56+
ids=device_ids)
5857
id_labels = [meta[col]['heading'] for col in ['id', 'cycle', 'start_time']]
5958
data_labels = _data_labels_from_meta(meta, id_labels)
6059
cycles_df = _create_multi_index_df(id_labels, multi_ids, data_labels, vals)
6160
return cycles_df
6261

6362

64-
def create_outside_df(dict_or_pickle_file, location_ids=None):
63+
def create_geospatial_df(dict_or_pickle_file, location_ids=None):
6564
"""Returns pandas DataFrame containing records with location IDs and time
6665
stamps as multi-part indexes and outdoor temperatures as values.
6766
@@ -71,21 +70,21 @@ def create_outside_df(dict_or_pickle_file, location_ids=None):
7170
location_ids (Optional[list or other iterable of ints or strings]): Location IDs. If no argument is specified, all IDs from the first arg will be in the DataFrame.
7271
7372
Returns:
74-
outside_df (pandas DataFrame): DataFrame has MultiIndex based on the ID(s) and timestamps.
73+
geospatial_df (pandas DataFrame): DataFrame has MultiIndex based on the ID(s) and timestamps.
7574
"""
7675
multi_ids, vals, meta = _records_as_lists_of_tuples(dict_or_pickle_file,
77-
list(Outside._fields),
76+
list(Geospatial._fields),
7877
ids=location_ids)
7978
id_labels = [meta[col]['heading'] for col in ['id', 'time']]
8079
data_labels = _data_labels_from_meta(meta, id_labels)
81-
outside_df = _create_multi_index_df(id_labels, multi_ids, data_labels, vals)
82-
return outside_df
80+
geospatial_df = _create_multi_index_df(id_labels, multi_ids, data_labels, vals)
81+
return geospatial_df
8382

8483

8584
def _records_as_lists_of_tuples(dict_or_pickle_file, fields,
8685
ids=None):
8786
"""Returns tuple containing
88-
1) a list of named tuples containing thermostat (or outdoor location) ids
87+
1) a list of named tuples containing sensor (or outdoor location) ids
8988
and timestamps and
9089
2) a list of either indoor (or outdoor) temperatures, or the ending time
9190
of a cycle, based on input of a pickle file containing a dict.

0 commit comments

Comments
 (0)