Zeo Sleep Monitor - Android Data Export

Hi John,

When you refer to a full dB dump, what data are you referring to?

Also, I do not believe I can recompile the original zeo app. I’ve spent about 10 hours trying to run the sources, but too much information is lost in the decomilation process. Some classes are exported as byte code.

What do you mean by recompile Zeo wth the same signature as ZeoCSV? What would this achieve?

Cheers.

Thanks Nicholas!

I’ve been playing with the CSV export so that I can dump it into Splunk and do analtyics. I’m using Zeo CSV -> AutoShare -> FileSync (SFTP) -> linux VM -> python script (converts the data to a more splunkable format) -> Splunk.

The end result is I click the share button in the app, choose AutoShare, and my data shows up in Splunk. It’s not perfect, but does work.

While clicking around, I noticed that the values that are in minutes (Total_Z, Time_in_REM, etc) are doubled. I double checked the source data to make sure it wasn’t my conversions doing it, and it’s in the source CSV as well.

Thoughts?

Thanks!

Hi Alexander,

I’ve noticed this too, but in addition, the values are doubled in only some sleep events.
I am not manipulating the raw data provided by the Open API, so think there may be some error in the ZeoContract providing the relevant data.

What I am currently doing is performing subtracting the start datetime from the end datetime, which should be larger than Total_Z.
If it is not, I will halve all the minute values.

I’ll post again when i’ve pushed the update :slightly_smiling:

Cheers,
Nicholas

Hi Alexander,

I’ve just submitted an updated version that should resolve the issue.
Let me know how it goes :slightly_smiling:

Cheers,
Nicholas

The full data dump “I believe” is more a teaching/learning tool for people who want to know “everything” the Zeo app collects from the Zeo Mobile device.

For example: I know if I get a phone running Android KitKat 4.4 and installed SuperSU on that phone, I can grant “root” access to a file explorer like ES Explorer and [go get] the zeo.db sqlite database, even though it is “owned” and stored in the {private} data directory of the Zeo app.

But not everyone has an Android KitKat 4.4 device, they have something newer, or can’t downgrade below Android 5.0 because that was what their device was manufacturered to start with, older versions won’t support their Android Device.

Recompiling the Zeo app should be possible, its not obfuscated, and its pretty plain code from just causually “looking at it” – in fact its pretty verbose about everything it does – like they wanted you to read it.

Try using jd-gui from github rather than dex2jar, the result is far cleaner. It does not produce a ‘project’ but its another avenue to explore.

I’m no Android coding expert, but it seems a very social thing to do to “recompile” other peoples apps to add functionality, or “read” them to learn how to use things. If they sold the app there might be some hesitation… but since the company has shutdown and sold the tech, and this device will probably never be made or supported again. And the fact they made no effort whatsoever to make it difficult read the code… leads me to think they think of it more as a “Users Manual”.

I also don’t think anyone has any reason to copy the code, recompiling technically isn’t “copying the code” adding to it is an Android API, they even made “contracts” available… and Zeo supports a “contract” to export a nice clean set of tables and cleaned up “summary” table… they seemed to be building bridges in everyway possible. Publishing the “open zeo” code to github documented an “exra” effort they made to make the raw tables available within the Android ecosystem.

The zeo.db has 12 tables, most of which are empty, or don’t look like they are used, probably intended for deprecated features, or future expansion of features… by their names… looks more like the former. So only four tables have data, and the fifth they made available with the “contract” api was a “cleanup” join from data in all the other tables, organizing scattered data.

With all this in mind, someone mentioned “what if” Android 7.0 won’t run the app, we would need to “recompile” to keep on using it. That plus supporting the “curious” who want the “raw” database makes it a worthy project.

Everything in Android “is an intent” and an “activity”… my guess is we’d want to [add] a “share” menu item in the settings or other pulldown menu, to least disturb the current code… all it need do is grab the zeo.db and offer to share it like the ZeoCSV program does. All about “adding to” not about modifying or taking away from the existing app.

The reason why they didn’t do this in the original app, would be… my guess… they felt they needed to “further explain” or cleanup the dataset so people could use it… and they felt just giving access “wasn’t enough” reason to justify releasing a new version. It also kept the original app “smaller” since that feature wasn’t a feature most people would use – raw data access, rather they would prefer the “cleaned up” version of data export. The raw database format also wasn’t in a form every operating system and user would understand as sqlite.db where exporting it as a file.csv opened up the ability to import it with many familar tools.

Nicholas,

Awesome, at a glance, it looks like it’s fixed! I checked a couple of days of data and it seems like it’s working well now.

Thank you for putting this together, and thank you for the quick response. Because Steve Gibson has been talking about the Zeo and the need for something like Zeo CSV on his Security Now! podcast, I’m sure there are a number of people clamoring for something exactly like this.

For future enhancements, you may want to consider exposing an intent to just write the CSV to disk somewhere and / or doing a Tasker plugin. From reading the documentation, the original Zeo app may send an intent when the headband is docked (I haven’t tested yet). If so, and if your app could be invoked problematically, it opens the door to the entire process being automated.

Thanks again!

Hi John,

After reading the teardown from the German researcher, I understand that most of the processing is done in the actual headband, and as such any data in the sqlite db will be similar (or the same) as what I can provide in ZeoCSV.

If there is any particular data that you would like from ZeoCSV - let me know, and i’ll do my best to add the relevant functionality.

Recompiling the original software would be possible, but would require a great deal of experience in the matter. After my efforts in trying about 5 different tools (including jd-gui), none are able to completely decompile the code, and have some byte-code included.
Additionally, many of the try-catch blocks break, as well as many classes and methods being renamed.

Further to the above, there is the matter that a decompilation -> recompilation would have possible copywrite implications, I understand that ResMed purchased the IP from Zeo.
Even if I was successful in recompiling the app, I would not be comfortable in publishing an APK on the Play store.
Perhaps if we wish the source, it may be worth asking someone such as Steve to contact ResMed to release the source, and then we would be able to successfully, and ethically reproduce the code.

On another track, I have finally had some success in setting up a BT server socket that receives data sent by the Zeo.
I obtained much the same data that you reported in your most recent blog posting.

This is the output that I received after docking-undocking-placing on head-docking

> 02-27 17:33:40.825 5825-5895/au.com.nsby.zeosocket V/MainActivity: current result: 

> HMSG_êa Ïx                      X                                                                                                                                    ¸=ÑV                                                                                  DDDDDDDDDDDDDDDDDDDDDDDDDDD333333333333333333333333333333333333333333DDDDD4333333333333333DDD3333"""""""""""""""""""""""""""2333333333333333333333333CDDDDDDDD4333333333333333333333333333333333333333333#"""""""""""""""""""""""3333333333333333333333333333333333333333333DDDDDDDDDD333333333333333333333333""""""""""""""""""""""3333333333333333333333333333333333#"""""""""""""""""""""""""""""""""""""""""23333333333333333333                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               HMSG%	 Ð        W Ð HMSG– Ñ    HMSGžP Ò    HMSGºT Ó 
>                                                                       HMSG–  Ô    HMSG'ð Õ    HMSGX‘ Ö    HMSGx ×    HMSG& Ø a   HMSGm€ Ù    HMSG’ Ú    HMSG%ˆ Û    HMSGÕç Ü a   HMSG³aa Ýxä>ÑV                  X                         @ÑV                                                                                                     ä>ÑV           ¹?ÑV   
>                                                                                                                                 DDDDDDDDDDDDDDDDDDDDDDDDDDD333333333333333333333333333333333333333333DDDDD4333333333333333DDD3333"""""""""""""""""""""""""""2333333333333333333333333CDDDDDDDD4333333333333333333333333333333333333333333#"""""""""""""""""""""""3333333333333333333333333333333333333333333DDDDDDDDDD333333333333333333333333""""""""""""""""""""""3333333333333333333333333333333333#"""""""""""""""""""""""""""""""""""""""""23333333333333333333                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               HMSG‚ñ Þ

Hi Alex,

Thanks for the feedback.

I actually have very little experience in Android programming, I had a unit of Java programming at Uni (about 5 years ago), so understand the language, but not some of the nuances of Android.

I will defiantly look into providing some means of an automated write CSV to disk.
In terms of exposing an Intent - is this basically publishing an API that other apps call to obtain data?

Cheers,
Nic

Nicholas,

I can totally understand your view on the matter.

Steve Gibson did mention he has talked with some CEOs on occasion, perhaps he could get them to pass judgement on the matter and release the code.

I also don’t think there is much to be gained by just exporting the db and only see it as instructive.

A totally new Android app would be preferrable. As would an iOS app, Windows and OSX, Linux ect…

Congratulations on the RFCOMM approach, that looks to me the easiest avenue. And very nice sleep record dump!

I’m also giving some thought to the firmware on the Zeo, since there must be a DFU some place for programming it and updating it. I just haven’t got a good handle on where it is at the moment. I am thinking those SPP services might be the key.

Hey do you ever use an nntp reader/posting program?

I’ve been sort of posting updates on your progress over on the nntp thread grc.health on Steve Gibsons news server news.grc.com, there is real interest and I have posted the QS link to this thread so they could come watch the conversation too.

Several people got your Amazon Fire pub and report good success with it.

In all honesty I think its the primary method anyone non-technical is getting the data off their device now.

There has been some talk about the 5 min vs 30 sec resolution of the data, but I’ve not really [looked at] the data… as I’ve been interested in code and hardware.

You might have more to contribute to help them.

Nicholas,

http://tasker.dinglisch.net/index.html
http://tasker.dinglisch.net/plugins.html

I own a copy of Tasker and I think its usable with your ZeoCSV as it currently is with some wicked gyrations, but a plugin would be a lot more friendly to users.

Its always great to further what you know vs don’t know.

My priorities are mainly to learn more about the hardware, but helping people better use whats available is also very kind.

Hi Nicholas,

Yup, intents are the way apps communicate with each other on Android.

Tasker is a visual scripting tool that supports plugins. It can set and react to much of what’s going on in your phone, and can also react to or set intents sent by other apps.

As an example, for the last couple of years, I’ve been using Sleep As Android to track sleep (I just got the Zeo). When I start sleep tracking in Sleep As Android, it tells Tasker (via a plugin, intents would work too, but aren’t as user-friendly) that it has started sleep tracking. Tasker has a profile set up to listen for this. When sees it, it runs a task that sets my phone to priority notifications only, and via another plugin, tells my home automation system that I’m sleeping, which then turns off my nightstand light.

What I’m most interested in for Zeo CSV would be an intent and/or Tasker plugin that could be used to write the CSV. This way, I could work it into an automated task that pushes the CSV out of Zeo and on to my server for processing without having to click the button (not that clicking the button once a day is that hard, I’m just an automation geek).

  • Alex

Hye Nicholas,

Just a quick note - I was reading the documentation last night, and it looks like those sleep values are the number of 30 second epochs, and not minutes (which is confusing). So, it makes sense that you would need to cut the values in half to get minutes out of it.

Thanks again!

The Zeo Headband Bluetooth protocol data formats have been decoded from the Android App. However, I cannot upload it as I’m a “new user”. So, here is the text below:

Zeo Headband Bluetooth Protocol for Zeo-Mobile
Document dated 3 Mar 2016

Bluetooth Protocol Stack

Length (bytes)	Protocol layer
--------------	--------------
1		HCI H4	
4		HCI ACL
4		L2CAP
4		RFCOMM header
variable	Zeo Base Record Format (see below)
1		RFCOMM trailer

Zeo Base Record Format

Minimum record length = 12 bytes
Length (bytes)	Format		FieldName		Notes
------		------		---------		-----
4		char		Identifier		always "HMSG"
2		int 16 bit	CRC
1		int 8 bit	Protocol_Verion		always 2
1		enum 8 bit	Message_Type		see below
1		int 8 bit	Ack_Request
1		int 8 bit	Sequence_No
2		int 16 bit	Data_Content_Len	may be 0
varible		variable	Data_Content		may be none


Message_type (each format broken out below)
------------
Dec	Hex	Message_type			Data_Content_Len
---	----	------------			----------------
0	0x00	NULL				0
1	0x01	COMMAND_REQUEST			4
2	0x02	HB_ACKNOWLEDGE			4
3	0x03	HB_STARTING			104
4	0x04	HB_STATE_CHANGE_REPORT		4
5	0x05	REPORT_ACCELEROMETER		72
6	0x06	REPORT_ERROR			4
7	0x07	SLEEP_REPORT			1144
8	0x08	SET_UP_BLUETOOTH		56
9	0x09	STATE_REPORT			16
10	0x0A	TIME_QUERY			8
11	0x0B	TIME_REPORT			12
12	0x0C	WAKEUP_NOTIFY			4
13	0x0D	WAKEUP_WINDOW			12
163	0xA3	LED_ON				4
Note:	the following message types are for "Test mode", and their
	Data_Content formats and length were not documented;
	however information about their likely contents are
	documented for certain messages in:
		Zeo Data Decoder Library Documentation; August 4, 2010
150	0x96	TEST_FIRST
151	0x97	TEST_ACCEL_QUERY
152	0x98	TEST_ACCEL_REPORT
153	0x99	TEST_ACP_QUERY
154	0x9A	TEST_ACP_REPORT
155	0x9B	TEST_ANALOG
156	0x9C	TEST_BC6_PASSTHROUGH
157	0x9D	TEST_BT_ADDRESS_SET
158	0x9E	TEST_BUTTON_REPORT
159	0x9F	TEST_CAL_DATA_QUERY
160	0xA0	TEST_CAL_DATA_REPORT
161	0xA1	TEST_FREQ_TRIM_SET
162	0xA2	TEST_IMPEDANCE
163	0xA3	TEST_LED			4
164	0xA4	TEST_PCB_TEST_MODE
165	0xA5	TEST_POWER_OFF
166	0xA6	TEST_RESET
167	0xA7	TEST_SERIAL_ID_SET
168	0xA8	TEST_VOLTAGE_QUERY
169	0xA9	TEST_VOLTAGE_REPORT
170	0xAA	TEST_WAVEFORM

NULL Message_type

Data_Content_Len = 0
Data_Content format:  none

COMMAND_REQUEST Message_type

Data_Content_Len = 4
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		enum 8 bit	Command			see below
	3		zero					padding; should be 0

Command
-------
0	0x00	ZEO_COMMAND_NONE
1	0x01	ZEO_COMMAND_BLUETOOTH_LOCK
2	0x02	ZEO_COMMAND_BLUETOOTH_UNLOCK
3	0x03	ZEO_COMMAND_DEMO_MODE_OFF
4	0x04	ZEO_COMMAND_DEMO_MODE_ON
5	0x05	ZEO_COMMAND_FACTORY_RESET
6	0x06	ZEO_COMMAND_QUERY_STATE
7	0x07	ZEO_COMMAND_RESET_SENSOR_USE
8	0x08	ZEO_COMMAND_SLEEP_SEND
9	0x09	ZEO_COMMAND_SLEEP_START
10	0x0A	ZEO_COMMAND_SLEEP_STOP
11	0x0B	ZEO_COMMAND_NMAX
100	0x64	ZEO_COMMAND_REBOOT

HB_ACKNOWLEDGE Message_type

Data_Content_Len = 4
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		int 8 bit	Prev_Sequence_No	
	3		zero					padding; should be 0

HB_STARTING Message_type

Data_Content_Len = 104
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	20		char		Device_Name	
	6		zero					padding; should be 0
	2		int 16 bit	FlashCalibrationUpdates
	2		int 16 bit	FlashSavedDataUpdates
	2		int 16 bit	FlashSleepBackupUpdates
	4		int 32 bit	Hardware_Version
	12		char		Model
	16		char		Serial_ID
	4		int 32 bit	Software_Version
	8		int 64 bit	Headband_Address
	20		char		Last_Assert_Func
	4		int 32 bit	Last_Assert_Line
	1		enum 8 bit	Reset_Cause		see below
	1		bool 8 bit	Query_Response
	2		int 16 bit	Reset_Count

Reset_Cause
-----------
0	RESET_NONE
1	RESET_BROWNOUT
2	RESET_EXTERNAL
3	RESET_INTERNAL
4	RESET_LOCKUP
5	RESET_POWERUP
6	RESET_WATCHDOG
7	RESET_MAX

HB_STATE_CHANGE_REPORT Message_type

Data_Content_Len = 4
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		enum 8 bit	Event			see below
	3		zero					padding; should be 0

Event
-----
0	0x00	EVENT_NONE
1	0x01	EVENT_ALARM
2	0x02	EVENT_ALARM_WINDOW_ENDED
3	0x03	EVENT_ALARM_WINDOW_STARTED
4	0x04	EVENT_DOCKED
5	0x05	EVENT_LOW_BATTERY
6	0x06	EVENT_OFF_HEAD
7	0x07	EVENT_ON_HEAD
8	0x08	EVENT_SLEEP_MODE_CHANGED
9	0x09	EVENT_SLEEP_NIGHT_ENDED
10	0x0A	EVENT_SLEEP_NIGHT_RESTORED
11	0x0B	EVENT_SLEEP_NIGHT_SAVED
12	0x0C	EVENT_SLEEP_NIGHT_STARTING
13	0x0D	EVENT_SLEEP_ONSET
14	0x0E	EVENT_SLEEP_RATING_NEEDED
15	0x0F	EVENT_SLEEP_STATE_CHANGE
16	0x10	EVENT_TIME_JUMP
17	0x11	EVENT_UNDOCKED
18	0x12	EVENT_USER_SLEEP_LOCKED
19	0x13	EVENT_USER_SLEEP_OFF
20	0x14	EVENT_USER_SLEEP_ON
21	0x15	EVENT_USER_SLEEP_RESTART
22	0x16	EVENT_NUM

REPORT_ACCELEROMETER Message_type

Note: the two arrays store information every 30 seconds for 5 total minutes, thus 10 entries
Data_Content_Len = 72
Data_Content format:
	Total
	Length (bytes)	Array size	SubArray size	Format		FieldName		Notes
	------		----------	-------------	------		---------		-----
	40		10				int 32 bit	Diff_Vec_Mag_Sums	
	30		10		3		int 8 bit	Accelerometer X,Y,Z			
	1						int 8 bit	Sample_Average
	1						zero					padding; should be 0

REPORT_ERROR Message_type

Data_Content_Len = 4
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		enum 8 bit	Error_code		see below
	3		zero					padding; should be 0

Error_code
----------
0	ERROR_NONE
1	ERROR_INVALID_NAME
2	ERROR_INVALID_PIN
3	ERROR_PIN_NEEDED
4	ERROR_PIN_NOT_NEEDED
5	ERROR_RESET_NEEDED
6	ERROR_WRONG_CHANNEL
7	ERROR_MAX

SLEEP_REPORT Message_type

Data_Content_Len = 1144
Data_Content format:
	Max
	Length (bytes)	Array size	Format		FieldName			Notes
	------		----------	--------	-----				-----
	4				int 32 bit	Display_Hypno_Startime		Unix timestamp
	2				int 16 bit	Awakenings
	2				int 16 bit	Time_In_Deep			30-second intervals
	2				int 16 bit	Time_In_Light			30-second intervals
	2				int 16 bit	Time_In_REM			30-second intervals
	2				int 16 bit	Time_In_Awake			30-second intervals
	2				int 16 bit	Time_To_Z			30-second intervals
	2				int 16 bit	Total_Z				30-second intervals
	2				int 16 bit	ZQ_Score
	1				bool 8 bit	Sleep_Valid
	1				bool 8 bit	Valid_For_History
	1				int 8 bit	Voltage_Battery
	1				bool 8 bit	User_Initiated
	4				int 32 bit	Accumulators_Deep_Sum
	10		5		int 16 bit	Accumulators_Stage_Counts
	2				int 16 bit	Accumulators_Times_Woken
	2				int 16 bit	Accumulators_Total_Count
	2				int 16 bit	Accumulators_Total_Sleep_Count
	2				int 16 bit	Count_To_Z
	2				zero						padding; should be 0
	4				int 32 bit	End_Of_Night			Unix timestamp
	108				see below	Display_Hypnogram
	1				bool 8 bit	Insufficient_Data		A night flagged with Insufficient Data is not stored in sleep history
	3				zero						padding; should be 0
	4				int 32 bit	Insufficient_Data_Startime
	2				int 16 bit	Light_Changed_to_Deep
	2				zero						padding; should be 0
	4				int 32 bit	Start_Of_Night			Unix timestamp
	1				bool 8 bit	Valid
	3				zero						padding; should be 0
	964				see below	Base_Hypnogram

Display_Hypnogram
-----------------
	Note: each array entry (1 nibble) is a Sleep_Stage for a 5 minute interval
	Maximum length = 108 bytes
	Length (bytes)	Array size	Format		FieldName			Notes
	------		----------	--------	-----				-----
	4				int 32 bit	Hypnogram_Length		is the Array_size
	4				int 32 bit	End_Of_Night_Count
	variable	variable	enum 4 bit	Sleep_Stage			Max of 96 nibbles; see below
	4				int 32 bit	Start_Time			

Base_Hypnogram
--------------
	Note: each array entry (1 nibble) is a Sleep_Stage for a 30 second interval
	Maximum length = 964 bytes
	Length (bytes)	Array size	Format		FieldName			Notes
	------		----------	--------	-----				-----
	4				int 32 bit	Hypnogram_Length		is the Array_size
	variable	variable	enum 4 bit	Sleep_Stage			Max of 1920 nibbles; see below

Sleep_Stage
-----------
0	SLEEP_STAGE_UNDEFINED
1	SLEEP_STAGE_WAKE
2	SLEEP_STAGE_REM
3	SLEEP_STAGE_LIGHT
4	SLEEP_STAGE_DEEP
6	SLEEP_STAGE_LIGHT_TO_DEEP

SET_UP_BLUETOOTH Message_type

Data_Content_Len = 56
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	8		int 64 bit	Bluetooth_Address
	16		binary		Device_Link_Key
	20		char		Headband_Name
	8		char		Headband_Pin
	1		enum 8 bit	Setup_Type		see below
	3		zero					padding; should be 0

Setup_Type
----------
0	SETUP_BD_DATA
1	SETUP_NEW_NAME
2	SETUP_NEW_PIN
3	SETUP_PIN

STATE_REPORT Message_type

Data_Content_Len = 16
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		bool 8 bit	Active_Forced
	1		bool 8 bit	Bluetooth_Locked
	1		bool 8 bit	Demo_Mode
	1		bool 8 bit	Docked
	1		bool 8 bit	On_Head
	1		bool 8 bit	Requires_PIN
	1		bool 8 bit	Was_Charged
	1		bool 8 bit	Was_Queried
	1		int 8 bit	Headband_Voltage
	1		enum 8 bit	Headband_Voltage_Status	see below
	1		enum 8 bit	Last_Alarm_Reason	see below
	1		enum 8 bit	Last_Algorith_Mode	see below
	4		int 32 bit	Sensor_Use_Seconds

Headband_Voltage_Status
-----------------------
0	ZEO_VOLTAGE_NONE
1	ZEO_VOLTAGE_CHARGED
2	ZEO_VOLTAGE_CHARGING
3	ZEO_VOLTAGE_ON_BATTERY
4	ZEO_VOLTAGE_TOO_LOW
5	ZEO_VOLTAGE_NMAX

Last_Alarm_Reason	
-----------------
0	NONE	0
1	RISING_OUT_OF_DEEP
2	FROM_NONREM_TO_REM
3	FROM_REM_TO_NONREM
4	ALREADY_AWAKE

Last_Algorith_Mode
-----------------
0	IDLE
1	STARTING
2	RECORDING
3	ENDING

TIME_QUERY Message_type

Data_Content_Len = 8
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	8		int 64 bit	Current_Time		via System.currentTImeMillis()

TIME_REPORT Message_type

Data_Content_Len = 12
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	8		int 64 bit	Current_Time		via System.currentTImeMillis()
	1		bool 8 bit	Is_An_Offset
	1		bool 8 bit	Offset_Is_Negative
	1		int 8 bit	Query_Sequence_No
	1		zero					padding; should be 0

WAKEUP_NOTIFY Message_type

Data_Content_Len = 4
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		enum 8 bit	Wake_reason		see below
	3		zero					padding; should be 0

Wake_reason
-----------
0	WAKE_REM_TO_NONREM
1	WAKE_NONREM_TO_REM
2	WAKE_ON_WAKE
3	WAKE_RISING_OUT_OF_DEEP
8	WAKE_END_OF_WAKUP_INTERVAL

WAKEUP_WINDOW Message_type

Data_Content_Len = 12
Data_Content format:
Length (bytes) Format FieldName Notes
------ ------ --------- -----
4 int 32 bit Time_Msg_Sent
4 int 32 bit Window_Begin_Time
4 int 32 bit Window_End_Time

LED_ON Message_type

Data_Content_Len = 4
Data_Content format:
	Length (bytes)	Format		FieldName		Notes
	------		------		---------		-----
	1		bool 8 bit	LED_On
	3		zero					padding; should be 0
1 Like

Cool Mike,

I guess the thing to do would be to pick a language and deserialize this type of data into data structures.

Behaviorally the Headband has to pass through a fews phases, pairing, charging, reporting and presumably some type of command/request exchanges.

Android Apps being based on Intents and Activities probably makes its pretty obvious that you proceed from one task to another to get to a steady state, then fall into a routine for collecting data. I would guess the Android App hosts all of the collected data until it is sync’d with the cloud on a period, or on command.

Assuming we/people would want to recreate or port this valuable information to new native apps on other platforms its probably best to consider targets.

Recreating something on Android that is very open should be less work.

Creating something on iOS would be interesting.

A PC or Mac is essentially the same thing and I would guess either a C program. C# program and a Swift or Objective C program.

Python or Java “might” work… but always seems to alienate different groups of people eventually.

I’ve been burying sprinkler heads in the yard all weekend… so didn’t get a chance to look any further.

Seeing the data structures though really makes it look a lot simpler.

LOL, I just did part of that already. I captured the Bluetooth of a simulated sleep session just a few hours ago, and wrote a quick and dirty Wireshark Lua-dissector for the Zeo HB data protocol. I just finished extracting the interaction flow between the HB and the Tablet from the Wireshark decoding.

I’m going to augment the protocol document with the sample interaction flow. However, I think I need to do a 24 hour capture of both in-use and idle interactions between the HB and the Tablet in-case there are interactions during idle.

However, unless we can determine how to turn on “Test mode” in the HB, I’m not sure we can extract any additional useful information than is already in the Tablet’s database and available with the CSV utility.

I’ll post the results of the capture session later tonight. Though I cannot post files here yet.

  • Mike

Nicholas, Alex, John,
Are you’all willing to work on a joint GitHub project that would create a companion app for the existing Zeo app? There are several parallel needs that I think can all be served with one companion App.

  1. Provide for a more comprehensive and very user-friendly CVS data export and sharing facility for the “masses” (offering data ranges of exports, automated push of daily extracts, etc). Plus its own API for use by more advanced automation-focused users like Alex to invoke for programmatic data extraction.
  2. Provide a “I’m still awake” monitoring feature to compliment the Zeo headband for those of use who have trouble falling asleep, and for whom the Zeo rarely is able to properly determine whether we’ve fallen asleep yet or not.
  3. Provide a more advanced “troubleshooting” capability to the headband, allowing direct communications, commanding, and experimentation with the headband over bluetooth.

I’m personally interested in all three capabilities. Trying to re-purpose the existing Zeo app has concerns as Nicholas expressed since its copyright is still owned by a viable company. But creating a companion app that builds upon the existing APIs and augments the Zeo ecosystem seems a better path to follow.

I’ve got a fair amount of Android app development experience, and an extensive amount of iOS app development experience.

  • Mike

FYI … I wrote a LUA dissector for Wireshark for the Zeo Headband Bluetooth protocol. So now its quite easy to record and replay the headband and Android device interactions.

Just use the Android Developer Mode Bluetooth HCI snooping feature on the device, then pull the log file off the device and into a computer, then open the file with Wireshark. Voila, all the packet flows are dissected. One does have to force a “Decode as” for the RFCOMM.

I can’t post the LUA file here yet since I’m too new.

Hey Mike,

I’m always interested in furthering my GitHub skills… there is so much I don’t know about collaboration tools.

I’ve a long DOS, Windows background… and a really long Unix, Linux background… Apps are still kind of new to me (they all look like Java with pretty window dressing to me). So I have this awful tendency to prefer simple cmdline apps, or services that enable things in other scripts or languages.

Doing a [whole] App top to bottom just either seems so “one off” or just so fragile, they never seem to meet everyone’s needs.

To your point about an original Zeo app that is truly free, based only on the ‘discovered’ APIs… it is interesting. There should be an alternative for Android and iOS… I am just not sure what that should look like.

I would like to look at your Lua dissector and get it up and running.

Two projects on my plate right now are laying a trail of breadcrumbs for other people to follow… I tend to loose interest all to often… and think its the ‘right thing to do’ to make it easier for people who want to follow up later.

So what I want to do are these things

  1. document how to at least compile an rfcomm-server program using Visual C++ Express to receive the Zeo chatter - this wasn’t too hard because I found a demo app that interfaced with the native Microsoft bluetooth stack in the back of a book. So really it was just understanding and grabbing the unique UUID the MyZeo app writers advertised … and that was easy because Linux has a suite of cmdline tools to “search” rfcomm for services available within range.

  2. it really bothered me that people didn’t see how easy it was to look at the code of an Android app that doesn’t even use the obfuscation natively available… I was thinking of decompiling the native ZeoCSV app as an example… it might get people to use Eclipse or Android Studio more often both for learning and for compiling their own apps

  3. Tasker plugin… this is as close to a cmdline tool as I can see on Android, its widely available and scriptable for most people… a simple rfcomm-server that performed taskers bidding would be really cool and extensible

Your driving ahead really a lot faster than I am… so please take the lead. I’ll plod along at my slower pace and help if I can.

I was granted rights to post files now. I’ve attached the latest Zeo Headband Bluetooth Protocol document, and the Wireshark LUA dissector for the protocol. The protocol document has been augmented with a sample interaction flow between the headband and the android device for an example sleep session.

To use the dissector, you need to put the LUA script (on Windows) into “C:\Users<user>\AppData\Roaming\Wireshark\plugins”. After opening a btsnoop_hci.log file in Wireshark, one has to force a “decode as” L2CAP CID->BT RFCOMM. Then the Zeo dissecting should auto-active within the RFCOMM layer.

The dissector mostly works, but is a bit buggy … first one I ever wrote. And it has to perform its own packet re-assembly which I’ve kludged in a knowingly poor manner as I’ve not full worked out how to get the dissector to access information from prior layers (in order to know whether its a message-sent or a message-received since they are interlaced). I’ll fix that once I’ve looked over some more examples.

Also, to get all the expanded field elements in a multi-packet message (such as the SLEEP_MESSAGE), you have to click on just each packet of the overall message in-order top-down until the final one, then all the sub-fields will show in that final one. I’m not sure why Wireshark does not “remember” that since its doing it on its initial pass as seen in the “info” column. Another bug.

Zeo Headband Bluetooth Raw Data Format.txt (17.8 KB)
zeohbbt.lua (11.3 KB)

Awesome Mike

I’ll try this out tomorrow night. Its most excellent to have a reason to better understand how Wireshark works.

Lau makes me recall the Phillips TiVo box I once hacked… so cool

… Hey it might be an awful timezone conflict… but I’m going to attempt a Google Hangout on Saturday… just posted the offer over on grc. I invited the World… but seriously… how many could possibly truly be interested? Lol