Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eve Energy - Decoded Schedules #95

Open
An00bIS47 opened this issue Sep 13, 2020 · 5 comments
Open

Eve Energy - Decoded Schedules #95

An00bIS47 opened this issue Sep 13, 2020 · 5 comments

Comments

@An00bIS47
Copy link

An00bIS47 commented Sep 13, 2020

Elgato Eve Energy Schedules

The schedule seem to be almost similar to the Eve Aqua Schedules.

Required Characteristics:

  • Config Write: E863F11D-079E-48FF-8F27-9C2605A29F52
  • Config Read: E863F131-079E-48FF-8F27-9C2605A29F52

Data format

The schedule is handled as TLV8 data although exposed as data in the characteristics!

Example write config characteristic (=> from App):

450D0502 00000000 00020101 4B059646 5405161C 2C9F2449 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00441105 03000000 00000000 00000000 00000000

Example read config characteristic (=> to App):

00022400 0302b804 040c4256 31324a31 41303732 31320602 6c010704 0c100000 
0b020000 05010002 04902700 005f0400 00000019 02960014 01030f04 00000000 
450d0502 00000000 00020101 3c059646 5405151c 2c9f2449 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00441105 0c000503 3c000000 32c24242 a1933441 47110573 1b451cdf 
1cb81db4 0000003c 00000048 06050000 0000004a 06050000 0000001a 04000000 
00600164 d0045209 03009b04 d00f0300 d200

Available TLV8 types:

Type Description Energy Energy Strip
0x00 ??
0x02 ??
0x03 ??
0x04 Serial Number
0x05 ??
0x06 Memory Used
0x07 Rolled Over Index
0x09 ??
0x0B ??
0x9B ??
0x0F ??
0x19 ??
0x14 ??
0x1A ??
0x4A ??
0x5F ??
0x9C ??
0x0C Hostname
0x44 Commands
0x45 Programs
0x46 Days
0x47 ??
0x48 ??
0x60 Status LED
0xD0 Last Switch Activity
0xD2 End mark
0xD3 ??
0x98 Last Update
0x32 ??
0x33 ??
0xA8 ??

Memory Used

type = 0x06
length = 2

Number of history entries

Rolled Over Index

type = 0x07
length = 4

Index of oldest entry if rolled over, otherwise 0

End Mark

type = 0xD2

This seems to be a the end mark for a TLV.
The length is 0.

Last Switch Activity

type = 0xD0
length = 4

Time in seconds from last switch activity

timestamp last switching activity - reference time

EVE Time

type = 0x98
length = 4

Actual time, in seconds from last time update

timestamp last update - reference time

Toggle Schedules On/Off

type = 0x44
length = 17

Command to toggle schedules on/off.

Example TLV8:

T: 0x44
L: 17 (HEX 11)
V: 0x0503000000000000000000000000000000

Example Values:

var data = "0502000000000000000000000000000000"			// OFF
var data = "0503000000000000000000000000000000" 		// ON

Script:

// 
// EVE Energy Decode Schedule Toggle
// 
// thanks to https://github.com/simont77/fakegato-history/issues/90
// 
// TLV8
// Type: 44
// 
var colors = require('colors');

var data = "0502000000000000000000000000000000"			// OFF
var data = "0503000000000000000000000000000000" 		// ON

curIndex = 2
len = 2

process.stdout.write(data.substr(0, curIndex))
process.stdout.write(data.substr(curIndex, len).green)
process.stdout.write(data.substr(curIndex + len))
console.log("")

// 03 for ON
// 02 for OFF
var schedule = parseInt(data.substr(curIndex, len).match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16) & 0x1;
schedule ? console.log(colors.green("ON")) : console.log(colors.red("OFF"))

Output:

0502000000000000000000000000000000
0
0503000000000000000000000000000000
1

Set Status LED

type = 0x20
length = 1

Command to set the status LED. The actual value will be given back in type = 0x60

Example TLV8:

T: 0x20
L: 1 (HEX 01)
V: 0x64

Available Values

00: IF OFF OFF
21: IF OFF LIGHT
42: IF OFF MEDIUM
64: IF OFF BRIGHT
a1: IF ON  LIGHT
c2: IF ON  MEDIUM
e1: IF ON  BRIGHT

Script

// 
// EVE Energy Decode LED Status
// 
// thanks to https://github.com/simont77/fakegato-history/issues/90
// 
// TLV8
// Type: 20
// 
var colors = require('colors');

var array = ["00", "21", "42", "64", "a1", "c2", "e1"]

// OFF
// 00 == 00000000	

// IF ON 
// 21 == 00100001	// light
// 42 == 01000010	// medium
// 64 == 01100100	// bright

// IF OFF
// A1 == 10100001	// light
// C2 == 11000010	// medium
// E4 == 11100100	// bright


curIndex = 0
len = 2

for (var i = 0; i < array.length; i++) {
	data = array[i]

	process.stdout.write(data.substr(0, curIndex))
	process.stdout.write(data.substr(curIndex, len).green)	
	process.stdout.write((": ").green)

	var led = parseInt(data.substr(curIndex, len), 16);	

	var ifOn = ( led >>> 7 ) & 0x01
	var brightness = (led >>> 5) & 0x03

	var brightnessStr = ""


	switch (brightness){
		case 0:
			brightnessStr = "OFF"
			break;
		case 1:
			brightnessStr = "LIGHT"
			break;
		case 2:
			brightnessStr = "MEDIUM"
			break;
		case 3:
			brightnessStr = "BRIGHT"
			break;				
	}

	console.log(ifOn ? "IF ON " : "IF OFF", brightnessStr) 
}

Programs

type = 0x45
length = variable

There can be 7 programs (for each day 1) and up to 15 timers per program.

There are 4 different types of timers:

  • timer & 0x1F == 1 => OFF by time
  • timer & 0x1F == 5 => ON by time
  • timer & 0x1F == 3 => OFF by sunrise
  • timer & 0x1F == 7 => ON by sunrise

The offset for timed events is in seconds since 0:00, for sunset/sunrise +/- seconds.

Example TLV8:

T: 0x45
L: 11 (HEX 08)
V: 0x0502000000000081000500

Example values:

// Timed:
var data = "0502000000000081000500"					// 0:00 OFF
var data = "0502000000000081000500" 				// 0:00 ON
var data = "050200000000000201014B0596"				// 1O:00 OFF - 20:00 ON
var data = "050200000000008301C5764579458D" 		// 15:50 ON - 16:10 ON - 18:50 ON
var data = "050200000000000302010A013C858E0596" 	// 1:20 OFF - 8:00 OFF - 19:00 ON - 20:00 ON

// Sunrise / Sundown:
var data = "050200000000000201E707A307"				// sunrise - 15 min - ON - sunrise + 15 min - OFF

Script:

// 
// EVE Energy Decode Timers
// 
// thanks to https://github.com/simont77/fakegato-history/issues/90
// 
// TLV8
// Type: 45
// 
var colors = require('colors');

var data = "0502000000000081000500"					// 0:00 OFF
var data = "0502000000000081000500" 				// 0:00 ON
var data = "050200000000000201014B0596"				// 1O:00 OFF - 20:00 ON
var data = "050200000000008301C5764579458D" 		// 15:50 ON - 16:10 ON - 18:50 ON
var data = "050200000000000302010A013C858E0596" 	// 1:20 OFF - 8:00 OFF - 19:00 ON - 20:00 ON

// var data = "0502000000000083024508013CC53C85690596"											// 5 timers
// var data = "050200000000000303013CA544255B256A056C0596" 										// 6 timers
// var data = "050200000000008303013CC5494560256FC58C0596C5A3"									// 7 timers
// var data = "050200000000000304013C8560856FA56F2586C58C0596A59C" 								// 8 timers
// var data = "0502000000000083070509851F013C6545655BA56A0581258705888588258F0590059685A6E5AD" 	// 15 timers

// var data = "050300000000000201013C05960201259E459E"										// 2 programs
// var data = "050400000000000201013C05960201A59EC59E8301A5532588E59D" 						// 3 programs
// var data = "050800000000000201013C0596810025A1810025A1810025A2810025A1810025A1810085A1" 	// 7 programs

// var data = "050200000000000201E707A307"				// sunrise - 15 min - ON - sunrise + 15 min - OFF

// Timer count
//   	  1 == 0081 == 000100000 01 =  129 ==  1 * 128 + 1
//   	  2 == 0102 == 001000000 10 =  258 ==  2 * 128 + 2
//   	  3 == 0183 == 001100000 11 =  387 ==  3 * 128 + 3
//   	  4 == 0203 == 010000000 11 =  515 ==  4 * 128 + 3
//   	  5 == 0283 == 010100000 11 =  643 ==  5 * 128 + 3
//   	  6 == 0303 == 011000000 11 =  771 ==  6 * 128 + 3
// 		  7 == 0383 == 011100000 11 =  899 ==  7 * 128 + 3
//        8 == 0403 == 100000000 11 = 1027 ==  8 * 128 + 3
//  	 ...
//  MAX: 15 == 0783 == 111100000 11 = 1923 == 15 * 128 + 3


function timerTypeToString(timer_type){
	if (timer_type == 0){
		return "TIME";
	} else if (timer_type == 1) {
		return "SUN"
	}
}

curIndex = 14

programs = []


process.stdout.write(data.substr(0, 2))
process.stdout.write(data.substr(2, 2).blue)

var programCount = parseInt(data.substr(2, 2).match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16) - 1;


process.stdout.write(data.substr(4, curIndex - 4))

var pcount = 0;
for (var i = 0; i < programCount; i++) {
	
	process.stdout.write(data.substr(curIndex, 4).cyan);

	var timerCount = parseInt(data.substr(curIndex, 4).match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16) >>> 7;
	curIndex = curIndex + 4;

	var timerEvents = [];	

	for (var tcount = 0; tcount < timerCount; tcount++) {				

		var timer = parseInt(data.substr(curIndex, 4).match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16);

		
		if (tcount % 2 == 0) {
			process.stdout.write(data.substr(curIndex, 4).green)	
		} else {
			process.stdout.write(data.substr(curIndex, 4).magenta)		
		}
			

		var timer_state = (timer & 0x1f) >>> 2
		var timer_type  = ((timer & 0x1f) & 0x2 ) >>> 1
		
		if ((timer & 0x1f) == 1 || (timer & 0x1f) == 5) {				

			var timer_min = (timer >>> 5) % 60;					// Timer minute
			var timer_hr  = ((timer >>> 5) - timer_min) / 60; 	// Timer hour
			var timer_offset = ((timer >>> 5) * 60);    		// Seconds since 00:00

			var timerEvent = {"h": timer_hr, "min": timer_min, "offset": timer_offset, "type": timerTypeToString(timer_type), "state": timer_state ? "ON" : "OFF"}
			
		} else if ((timer & 0x1f) == 7 || (timer & 0x1f) == 3) {								

			var timer_sunrise = ((timer >>> 5) & 0x01);    // 1 = sunrise, 0 = sunset
			var timer_offset = ((timer >>> 6) & 0x01 ? ~((timer >>> 7) * 60) + 1 : (timer >>> 7) * 60);   // offset from sunrise/sunset (plus/minus value)
			var timerEvent = {"sunrise": timer_sunrise, "offset": timer_offset, "type": timerTypeToString(timer_type), "state": timer_state ? "ON" : "OFF"}
		}
		
		curIndex = curIndex + 4;
		timerEvents.push(timerEvent);
				
	} 	

	pcount = pcount + 1;

	var programEvent = {"program": pcount, "timers": tcount, "events": timerEvents};
	programs.push(programEvent);
}

console.log("")
// console.log(console_output)
console.log("programs: " + colors.blue(programCount))

for (var i = 0; i < programs.length; i++) {
	console.log(">>> program: " + programs[i].program)
	console.log("    timer:   " + programs[i].timers)
	console.log(programs[i].events)
	console.log("--------------------------------------------------------------------------");
}

Output:

050200000000000304013C8560856FA56F2586C58C0596A59C
programs: 1
>>> program: 1
    timer:   8
[
  { h: 8, min: 0, offset: 28800, type: 'TIME', state: 'OFF' },
  { h: 12, min: 52, offset: 46320, type: 'TIME', state: 'ON' },
  { h: 14, min: 52, offset: 53520, type: 'TIME', state: 'ON' },
  { h: 14, min: 53, offset: 53580, type: 'TIME', state: 'ON' },
  { h: 17, min: 53, offset: 64380, type: 'TIME', state: 'ON' },
  { h: 18, min: 46, offset: 67560, type: 'TIME', state: 'ON' },
  { h: 20, min: 0, offset: 72000, type: 'TIME', state: 'ON' },
  { h: 20, min: 53, offset: 75180, type: 'TIME', state: 'ON' }
]

Days

type = 0x46
length = 84

Active Days of each program

Example TLV8:

T: 0x46
L: 84 (HEX 54)
V: 0x05161C2C9F24490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Example values:

// every day
var data = "05181C2C9F24490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

// every day - sun
var data = "05181C2C9F24090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

Script:

// 
// EVE Energy Decode Days
// 
// thanks to https://github.com/simont77/fakegato-history/issues/90
// 
// TLV8
// Type: 46
// 

var colors = require('colors');

// every day
var data = "05181C2C9F24490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

// every day - sun
var data = "05181C2C9F24090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

curIndex = 8
len = 6

process.stdout.write(data.substr(0, curIndex))
curIndex = 0 + curIndex

var console_output = ""

// active days across programs
var unknown = (data.substr(0, curIndex));   // Unknown data for first 6 bytes
// byte swapped 
// bits 6 - 14
// >>> 4
var daynumber = parseInt(data.substr(curIndex, 6).match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16) >>> 4;

process.stdout.write(data.substr(curIndex, len).green)
process.stdout.write(data.substr(curIndex + len))
console.log("")
// console.log(daynumber)

// bit masks for active days
var mon = (daynumber & 0x7);
var tue = ((daynumber >>> 3) & 0x7)
var wed = ((daynumber >>> 6) & 0x7)
var thu = ((daynumber >>> 9) & 0x7)
var fri = ((daynumber >>> 12) & 0x7)
var sat = ((daynumber >>> 15) & 0x7)
var sun = ((daynumber >>> 18) & 0x7)

console.log("M T W T F S S")
console.log(mon, tue, wed, thu, fri, sat, sun)

Output:

05181C2C9F24090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
M T W T F S S
1 1 1 1 1 1 0
``
@simont77
Copy link
Owner

Very nice work! The schedule is a bit outside of fakegato-history scope, and in any case now I don't have to time to include it into the code. Could you add a wiki page on it?

@An00bIS47
Copy link
Author

Hi @simont77,
there are still some parts required to get this working. When there are all required parts decoded, I can add it to the wiki, but for now, I will update this issue from time to time.

Maybe someone can help out with decoding the missing parts :)

@n0rt0nthec4t
Copy link

@An00bIS47 Thanks for the credit. Glad my initial work on the Aqua schedule decoding helped

@tobekas
Copy link

tobekas commented Nov 22, 2020

Great!
@An00bIS47: Can you also post an example "read config" dump from the "Eve Energy Strip" here? Especially an example with a combined schedule for all 3 outlets would be interesting...

Additional infos:

  • Type 0x00 is device type. (01=Weather, 02=Room, 03=DoorWindow, 04=ENERGY-EU, 0b=Lightswitch, 0c=Thermo, 0d=ENERGY-US, 11=Motion, 12=ENERGY-UK, 14=ENERGY-EU, 18=Smoke, 1a=ENERGY-AU, 22=Lightswitch, 23=Aqua, 24=ENERGY-EU, 25=Motion, 26=DoorWindow, 27=Room_2, 28=ENERGY-UK, 29=ENERGY-AU, 2c=Thermo, 31=BUTTON, 33=Flare, 36=Light_Strip, 37=Energy_Strip-EU, 38=Energy_Strip-US, 39=Lightswitch, 3b=Extend, 47=Thermo) similar to the first byte in Firmware Characteristic E863F11E (in some accessories).
  • Type 0x03 is firmware build number.

@tobekas
Copy link

tobekas commented Nov 23, 2020

  • Type 0x44 can also include timezone and location (if configured in Eve App settings):
    example set config: 44 11 05 1500 0000 3c000000 91365242 a6b15441

    • 3c000000: 0x0000003c = 60min = UTC+1h
    • 91365242: 0x42523691 (IEEE-float) = 52.5532875° Latitude
    • a6b15441: 0x4154b1a6 = 13.2933712° Longitude
    • I'm not sure about the first 15. It might mask which data are valid. Without timezone data, bit 0x04 is not set. If bit 0x02 is set, bit 0x01 represents the schedule state (on/off)
  • Type 0x47 seems to be DST (daylight saving time) information, which are derived from location. App sends this to the accessory once:
    example: 47 11 05 731b 451c df1c b81d 78000000 3c000000

    • 0x1b73 = 7027 = 29.03.2020 DST begin
    • 0x1c45 = 7237 = 25.10.2020 DST end
    • 0x1cdf = 7391 = 28.03.2021 DST begin
    • 0x1db8 = 7608 = 31.10.2021 DST end
    • 0x00000078 = 120 = 02:00 time
    • 0x0000003c = 60 = +1h offset

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants