Skip to content

Instantly share code, notes, and snippets.

@andrey-yantsen
Last active June 27, 2021 21:13
Show Gist options
  • Save andrey-yantsen/0e1e18748f5e2f4c0a21a847c6cb38f6 to your computer and use it in GitHub Desktop.
Save andrey-yantsen/0e1e18748f5e2f4c0a21a847c6cb38f6 to your computer and use it in GitHub Desktop.
meta:
id: timeguard
file-extension: tg
endian: le
bit-endian: le
doc: |
The device (in my case — NTT WIFI) communicates with a binary API located at
www.cloudwarm.net:9997. The device tries to establish a TCP-connection, and
when it fails — goes with UDP. At the time of writing the TCP port was
not available. I tried forcing the device to use TCP by opening the port in
socat and forwarding it to the UDP on the Cloudwarm server, this seemed to be
working just fine.
Legal Disclaimer
This information is un-official and is not endorsed or associated with
Timeguard Limited in any way shape or form.
This information has been gathered legally using the Supplymaster iOS
application, NTT WIFI timeswitch and tcpdump/socat.
This information has been captured to aid my own personal efforts to automate
scheduling of my NTTWIFI device.
The information is provided “as is”, without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement. in no event shall the
authors or copyright holders be liable for any claim, damages or other
liability, whether in an action of contract, tort or otherwise, arising from,
out of or in connection with the informations or the use or mis-used or other
dealings in the information.
seq:
- id: hdr
type: header
- id: total_payload_size
type: u2
- id: message_id
type: u4
doc: |
Incrementing counter. Device always sends something here, starting with 1.
Server always sends 0xFFFFFFFF.
- id: payload
type: payload
- id: checksum
doc: |
CRC16 XMODEM of the payload
type: u2
- id: ftr
type: footer
enums:
work_mode:
0: auto
1: always_off
2: always_on
3: holiday
types:
header:
seq:
- id: magic
contents: [0xfa, 0xd4]
payload:
seq:
- id: message_type
type: b4
enum: message_type
- id: reserved1
type: b4
- id: is_success
type: b1
- id: is_update_request
type: b1
- id: unknown1
type: b1
doc: |
Almost always this bit is set to 1, except for a few rare exceptions
- id: is_from_server
type: b1
- id: reserved2
type: b4
- id: params_size
type: u2
- id: seq
type: u1
- id: unknown2
size: 3
- id: device_mac
type: u4
- id: params
type:
switch-on: "message_type.as<u1> + (is_from_server.as<u1> << 4) + (is_update_request.as<u1> << 5)"
cases:
32: ping_device
48: ping_server
5: schedule # response from device to schedule read requests
37: schedule # response from device to schedule update requests
53: schedule # schedule update request from server
21: schedule_server_read
29: boost_server
45: boost_device
40: work_mode_change
56: work_mode_change
11: schedule_id # response to "get current schedule"
27: empty # "get current schedule" command from server
43: schedule_id # response to "set current schedule"
59: schedule_id # "set current schedule" command from server
9: holiday # device response to "get holiday"
25: empty # "get holiday" command from server
41: holiday # "set holiday" response from device
57: holiday # "set holiday" command from server
44: advance # device response to "set advance mode"
60: advance # "set advance mode" command from server
42: schedule_id # device response to "update schedule name"
58: schedule_name
34: init # first message from the device to server with "initialization sequence"
50: init # server response to the first message
18: empty # server asks device to repeat "initialization sequence"
2: init # device repeats the "initialization sequence" to the server
size: params_size
instances:
params_object_type_id:
value: "message_type.as<u1> + (is_from_server.as<u1> << 4) + (is_update_request.as<u1> << 5)"
enums:
message_type:
0: ping
2: init # details are unknown
4: unknown
5: schedule
8: work_mode
9: holiday
10: update_schedule_name
11: active_schedule
12: advance
13: boost
empty: {}
init:
seq:
- id: unknown
size: 13
advance:
seq:
- id: is_enabled
type: b1
- id: reserved
type: b7
schedule_name:
seq:
- id: schedule_id
type: u1
- id: schedule_name
type: str
size: 50
doc: |
The name is limited to 40 symbols in the app, so the size could be
just 40-41 bytes here, and not 50
encoding: ASCII
schedule_id:
seq:
- id: schedule_id
type: u1
holiday:
seq:
- id: is_active
type: b1
- id: reserved
type: b7
- id: unknown1
size: 3
- id: ts_end
type: u4
- id: ts_start
type: u4
boost:
doc: |
There're 2 types of Boosts: 1-hour and 2-hours. Minutes are calculated
starting from the midnight of the last Sunday, e.g. 7155 means Thursday
23:15 — 7155 div 1440 == 4, 7155 % 1440 == 1395 minutes.
seq:
- id: minutes_from_sunday
type: b14
- id: is_1h_boost
type: b1
- id: is_2h_boost
type: b1
boost_server:
seq:
- id: boost_type
type: u1
enum: boost_type
enums:
boost_type:
0: off
1: on_1_hour
2: on_2_hours
boost_device:
seq:
- id: expected_finish_time
type: boost
doc: |
Could be other message, but so far I seen only the following values:
is_2h_boost=1
is_1h_boost=1
while the resulting time seems to be correct.
- id: boost
type: boost
ping_device:
doc: |
The device sends PING-requests every ≈41 seconds (sometimes less,
sometimes more). The "is_update_request" is always set. If the server
doesn't respond device continues as normal, without retries.
seq:
- id: status
type: b2
enum: device_status
- id: unknown1 # could be just reserved, seems to be always 0
type: b1
- id: load_detected
type: b1
- id: advance_mode_enabled
type: b1
- id: load_was_detected
type: b1
doc: |
Indicates whether load was detected during current (if power is on
right now), or the previous "session".
- id: reserved1
type: b2
- id: unknown2
size: 2
- id: work_mode
type: u1
enum: work_mode
- id: unknown3
size: 4
- id: device_uptime
type: u4
- id: boost
type: boost
- id: unknown4
size: 2
enums:
device_status:
1: off
2: on
ping_server:
seq:
- id: timestamp
type: u4
schedule_server_read:
seq:
- id: schedule_id
type: u1
repeat_weekdays:
seq:
- id: monday
type: b1
- id: tuesday
type: b1
- id: wednesday
type: b1
- id: thursday
type: b1
- id: friday
type: b1
- id: saturday
type: b1
- id: sunday
type: b1
- id: reserved
type: b1
schedule_time:
seq:
- id: minutes
type: b12
- id: is_enabled
type: b1
- id: reserved
type: b3
single_schedule_item:
seq:
- id: start
type: schedule_time
- id: end
type: schedule_time
- id: repeat
type: repeat_weekdays
- id: reserved
type: u1
schedule:
doc: |
Schedule with an empty name == deleted schedule
seq:
- id: schedule_id
type: u1
- id: schedule1
type: single_schedule_item
- id: schedule2
type: single_schedule_item
- id: schedule3
type: single_schedule_item
- id: schedule4
type: single_schedule_item
- id: schedule5
type: single_schedule_item
- id: schedule6
type: single_schedule_item
- id: schedule_name
type: str
size: 50
doc: |
The name is limited to 40 symbols in the app, so the size could be
just 40-41 bytes here, and not 50
encoding: ASCII
work_mode_change:
seq:
- id: mode
type: u1
enum: work_mode
footer:
seq:
- id: magic
contents: [0x2d, 0xdf]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment