mirror of https://github.com/lumapu/ahoy.git
				
				
			
							committed by
							
								 GitHub
								GitHub
							
						
					
				
				 34 changed files with 2593 additions and 1773 deletions
			
			
		| @ -0,0 +1,3 @@ | |||
| #include "dbg.h" | |||
| 
 | |||
| DBG_CB mCb = NULL; | |||
| @ -1,194 +0,0 @@ | |||
| //-----------------------------------------------------------------------------
 | |||
| // 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
 | |||
| // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
 | |||
| //-----------------------------------------------------------------------------
 | |||
| 
 | |||
| 
 | |||
| // a) https://www.favicon-generator.org/
 | |||
| // b) exiftool -all:all= -r
 | |||
| // c) hexlify.py:
 | |||
| //    import sys
 | |||
| //    f = open (sys.argv[1], 'rb').read()
 | |||
| //    for n, c in enumerate(f):
 | |||
| //      if n % 16 == 0: print ('        "', end = '')
 | |||
| //      print (f"\\x{c:02x}", end = '')
 | |||
| //      if n % 16 == 15: print ('" \\')
 | |||
| //    if n % 16 != 15: print ('"')
 | |||
| 
 | |||
| #define FAVICON_PANEL_16 \ | |||
|         "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" \ | |||
|         "\x00\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f" \ | |||
|         "\x53\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61" \ | |||
|         "\x05\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80" \ | |||
|         "\x84\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea" \ | |||
|         "\x60\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x01" \ | |||
|         "\x9b\x50\x4c\x54\x45\xfc\xfe\xff\xff\xff\xff\xcb\xcd\xcf\x22\x25" \ | |||
|         "\x30\x12\x16\x21\x11\x15\x21\x11\x15\x23\x12\x16\x25\x10\x13\x1f" \ | |||
|         "\x8f\x91\x93\x9b\x9d\xa1\x14\x17\x22\x14\x18\x25\x14\x17\x23\x13" \ | |||
|         "\x17\x22\x14\x17\x24\x17\x19\x27\x22\x24\x2e\xc1\xc2\xc3\xf8\xfa" \ | |||
|         "\xfb\x61\x63\x6b\x11\x13\x1f\x15\x19\x24\x17\x19\x24\x18\x1a\x24" \ | |||
|         "\x18\x1b\x26\x16\x19\x26\x42\x44\x4b\xe7\xe8\xe9\xfd\xff\xff\xfe" \ | |||
|         "\xff\xff\xe0\xe2\xe4\x33\x36\x3f\x16\x19\x24\x16\x17\x22\x16\x18" \ | |||
|         "\x22\x17\x18\x23\x11\x12\x1e\x71\x73\x76\xfb\xfc\xfd\xb5\xb7\xba" \ | |||
|         "\x1a\x1c\x27\x15\x17\x22\x15\x17\x21\x14\x15\x20\x14\x16\x23\x17" \ | |||
|         "\x1a\x26\x1b\x1c\x25\xaa\xac\xad\x7c\x7f\x85\x11\x13\x1d\x16\x18" \ | |||
|         "\x24\x17\x19\x25\x18\x1a\x25\x30\x32\x38\xd7\xd9\xd9\xef\xf1\xf2" \ | |||
|         "\x47\x49\x51\x12\x13\x1e\x17\x19\x22\x16\x17\x23\x10\x11\x1c\x59" \ | |||
|         "\x5a\x5f\xf4\xf6\xf7\xcc\xce\xd0\x24\x25\x2e\x16\x18\x23\x19\x1a" \ | |||
|         "\x24\x16\x17\x21\x15\x16\x21\x17\x17\x21\x15\x17\x20\x92\x93\x95" \ | |||
|         "\x99\x9a\x9e\x16\x16\x20\x17\x19\x23\x1a\x1b\x25\x23\x25\x2c\xc4" \ | |||
|         "\xc6\xc7\xf8\xfa\xfa\x61\x62\x68\x15\x16\x20\x11\x12\x1d\x44\x45" \ | |||
|         "\x49\xea\xec\xec\xdf\xe1\xe2\x33\x35\x3b\x15\x16\x1f\x13\x14\x1d" \ | |||
|         "\x78\x7a\x7c\xfc\xfd\xfe\xb2\xb4\xb7\x1a\x1c\x26\x18\x1b\x25\x1a" \ | |||
|         "\x1b\x22\x19\x1b\x22\xae\xaf\xb0\x86\x88\x8b\x14\x16\x1e\x19\x1b" \ | |||
|         "\x25\x17\x19\x21\x17\x19\x20\x12\x13\x1a\x2c\x2d\x32\xcc\xce\xcf" \ | |||
|         "\xfb\xfe\xff\xd8\xdb\xdb\x72\x74\x76\x25\x26\x2c\x19\x1c\x24\x13" \ | |||
|         "\x16\x1d\x11\x13\x18\x4c\x4d\x4f\xc1\xc3\xc3\xe2\xe4\xe6\xf6\xf8" \ | |||
|         "\xf8\xb6\xb9\xb9\x4f\x51\x55\x1b\x1c\x23\x19\x1b\x23\x15\x17\x1f" \ | |||
|         "\x70\x71\x72\xbb\xbb\xbc\xc3\xc4\xc5\xde\xe0\xe1\xf6\xf8\xf9\xfd" \ | |||
|         "\xfe\xff\xe3\xe5\xe6\x7e\x81\x82\x21\x22\x27\x1d\x1f\x26\x94\x95" \ | |||
|         "\x95\xbd\xbd\xbe\xbd\xbe\xbe\xc8\xca\xcb\xe5\xe7\xe9\xfa\xfc\xfd" \ | |||
|         "\x52\x5f\xd3\xea\x00\x00\x00\x01\x62\x4b\x47\x44\x01\xff\x02\x2d" \ | |||
|         "\xde\x00\x00\x00\xd2\x49\x44\x41\x54\x18\xd3\x63\x60\x00\x02\x46" \ | |||
|         "\x26\x66\x16\x16\x56\x36\x76\x0e\x4e\x46\x06\x30\x60\xe4\xe2\xe6" \ | |||
|         "\xe1\xe5\xe3\x17\x10\x14\x82\x0a\x08\x8b\x88\x8a\x89\x4b\x48\x4a" \ | |||
|         "\x49\xcb\xc8\x82\xf9\x72\xf2\x0a\xdc\x8a\x4a\xca\x2a\xaa\x6a\xea" \ | |||
|         "\x50\x1d\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa\x06\x50\x1d\xb2\x86\x46" \ | |||
|         "\x5a\xbc\xc6\x26\xa6\x66\xe6\x72\x10\xbe\x85\xa5\x95\xb5\x89\xa9" \ | |||
|         "\x8d\xad\x9d\x3d\xc4\x08\x46\x07\x47\x27\x67\x17\x57\x37\x77\x0f" \ | |||
|         "\x39\xa8\x11\x9e\x4a\xa6\x36\x5e\xde\x3e\xbe\x7e\x50\x23\xfc\x03" \ | |||
|         "\x02\x55\x8c\x9d\x55\x82\x82\x43\xa0\x96\x86\x86\x85\x4b\x48\xb8" \ | |||
|         "\x84\x47\x44\x46\x41\x75\x44\xc7\x78\xc7\xba\x87\xc7\xc5\x27\x40" \ | |||
|         "\x75\xc8\x25\x26\x25\x7b\xa7\xa4\xa6\xa5\x67\x64\x42\x5d\x91\x95" \ | |||
|         "\x9d\x93\x92\x9b\x97\x5f\x50\x58\x24\x0c\x55\x52\x5c\x52\x5a\x56" \ | |||
|         "\x5e\x51\x59\x55\x5d\x53\x0b\x11\xa9\x93\xab\x6f\x68\x6c\x6a\x6e" \ | |||
|         "\x69\x6d\x6b\xef\x60\x00\x00\x01\x53\x2a\x2a\x63\x34\xcd\xf7\x00" \ | |||
|         "\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82" | |||
| 
 | |||
| #define FAVICON_PANEL_32 \ | |||
|         "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" \ | |||
|         "\x00\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a" \ | |||
|         "\xf4\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61" \ | |||
|         "\x05\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80" \ | |||
|         "\x84\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea" \ | |||
|         "\x60\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00" \ | |||
|         "\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00" \ | |||
|         "\x00\x07\x4a\x49\x44\x41\x54\x58\xc3\x9d\x97\xd9\x72\x5d\xc5\x15" \ | |||
|         "\x86\xbf\xee\xde\xd3\xd9\x67\xd0\x99\x74\x34\x3b\x48\x32\x48\xe0" \ | |||
|         "\x21\x60\xe1\x32\xd8\x15\x5e\x80\xdc\xf2\x00\xe4\x41\x12\x9e\x21" \ | |||
|         "\x37\xc9\x43\xa4\x20\x95\xe2\x9a\x82\x0c\x10\x70\x62\x06\x0f\xc2" \ | |||
|         "\x24\x15\xe2\x59\x96\xce\xbc\xe7\xb1\x73\x21\x93\x32\x20\x4b\xa7" \ | |||
|         "\xe8\xaa\x7d\xb3\x2f\x7a\x7d\xf5\xff\xfd\xaf\x5e\x2d\xf2\x52\x6b" \ | |||
|         "\x66\x58\x4a\xc0\x67\xff\xf8\x27\x6f\xbd\xf5\x36\xa3\xf1\x04\xad" \ | |||
|         "\x0b\x84\x94\x68\xad\xc9\xf3\x1c\xc7\x76\x08\xa3\x04\xdb\xb6\xc8" \ | |||
|         "\xf3\x12\x0d\xb8\x15\x9b\x28\x8c\x90\x52\xb0\xfe\xdc\x02\xbf\xff" \ | |||
|         "\xdd\x6f\xb9\x72\xf9\x32\xc5\x53\x15\xe5\x2c\xc5\xbf\x5b\xdf\x7e" \ | |||
|         "\x7b\x87\x34\x4b\x68\x36\xeb\x38\x8e\x4d\xb3\x59\xc7\xb6\x0d\x3a" \ | |||
|         "\x9d\x16\xb6\x63\x52\x71\x6c\xea\xb5\x2a\x52\x0a\x6a\xd5\x0a\x96" \ | |||
|         "\x65\x02\x1a\xc7\x36\xd9\xde\xda\x64\x6b\x6b\x9b\xf2\x07\x7b\x1a" \ | |||
|         "\xb3\x16\xcf\x8a\x82\x4f\x3f\xbd\x4a\x18\x86\x34\x1a\x73\x68\x24" \ | |||
|         "\x52\x1a\x08\xa1\xa8\x54\x1c\xa2\x30\xa4\xd9\xac\xd3\x9d\xef\xa0" \ | |||
|         "\xb5\x66\x65\x65\x91\x30\x8c\x50\x52\xd3\x6a\xd5\xd9\xda\x7a\x81" \ | |||
|         "\x56\xbb\xcd\x0f\xf5\x9e\x09\x40\x0a\x78\xb4\xf7\x98\x8f\x3f\xfe" \ | |||
|         "\x8c\xa2\x28\x99\x4c\xa6\x94\x25\xe4\x59\x49\x14\x47\xc4\x51\x4c" \ | |||
|         "\x92\x24\x54\x9c\x0a\xfd\xfe\x88\x3c\xcb\x89\xc2\x88\x2c\xcb\xd1" \ | |||
|         "\x5a\x23\x74\xce\xf6\xf6\x36\x4a\xf0\x3d\xf9\x67\x06\x10\xc0\xd7" \ | |||
|         "\xb7\xbf\xa1\x3f\x18\xb1\xb0\x30\x8f\xef\xfb\x98\xa6\x49\x96\x6b" \ | |||
|         "\xda\x9d\x39\x6c\x4b\x31\x1c\x8e\xa9\xd5\xaa\x8c\xc7\x1e\xae\xeb" \ | |||
|         "\x50\x71\xab\x0c\xfa\x63\x4c\x43\xb2\x79\xfa\x39\x2e\xbd\xf6\x1a" \ | |||
|         "\x47\x1d\xb6\x99\x2d\xb8\x7a\xf5\x73\xee\xde\x79\x80\x6d\x9b\x64" \ | |||
|         "\x79\x86\x6d\xdb\x64\x59\x86\x28\x73\x22\xa1\xa1\x2c\xb1\x2d\x03" \ | |||
|         "\xd3\x54\x2c\x2e\xf6\x70\x6c\x87\xb2\x28\xa8\xd5\x1c\xce\x9f\x3b" \ | |||
|         "\xc3\xda\xda\x29\x4a\xfd\x13\x00\x84\x00\xcf\x0f\xb8\x7e\xfd\x3a" \ | |||
|         "\xbd\x85\x0e\x8e\x6d\x31\x99\x7a\x58\x96\xc5\x70\x38\x22\x8a\x63" \ | |||
|         "\xd2\x24\xc5\xb2\x4c\x1e\xde\x7f\x44\x9c\xa4\xa4\x71\x42\x91\xe7" \ | |||
|         "\x14\x65\x49\x96\xda\xac\xad\xad\xe2\x58\xe6\x8f\xe4\x9f\x09\x40" \ | |||
|         "\x02\xb7\xbf\xf9\x86\x4f\x3e\xb9\x4a\x92\xc4\x68\x5d\xd2\x68\xd4" \ | |||
|         "\x71\x9c\x0a\x4a\x49\x1c\xdb\xe2\xe0\x60\xc0\xe2\x52\x0f\x7f\xea" \ | |||
|         "\xe3\xe4\x19\xae\x5b\xe5\xe0\x60\x88\x10\x8a\x4e\xbb\xc5\xeb\x97" \ | |||
|         "\x2f\x3f\x73\xff\x99\x2c\xd8\xdd\xbd\xcd\x70\x34\x46\x6b\xcd\x74" \ | |||
|         "\xea\x63\x99\x16\x79\x9e\x63\x5b\x06\x9e\x80\x2c\xcb\x99\x4e\x7d" \ | |||
|         "\x92\xa4\xa0\xd9\x6a\xb1\xd0\xeb\x90\x65\x39\x96\x65\xb0\xb3\xf3" \ | |||
|         "\x73\xce\xbc\x74\xe6\x47\xf1\x9b\x19\x20\x2b\x0a\xbe\xf8\xe2\x3a" \ | |||
|         "\x95\x4a\x85\xb9\x46\x95\xfe\x60\x48\xb7\xdb\x65\x32\x1a\xa1\x94" \ | |||
|         "\x62\x3a\x0d\x90\x52\x12\x85\x11\x7e\x90\x50\x14\x05\xfd\xbd\x3d" \ | |||
|         "\xb2\x3c\xa3\xde\x70\x59\x5b\x5b\x65\xae\x39\xc7\xb3\xda\xdd\xb1" \ | |||
|         "\x00\x52\xc0\x83\x47\x7b\x7c\xf8\xd1\xdf\x18\x8f\x27\x44\xbe\x07" \ | |||
|         "\x42\xe1\xfb\x11\x85\x96\x74\xda\x6d\xb4\x50\xb8\xae\x4d\x51\x94" \ | |||
|         "\x98\x56\x82\xeb\x3a\x0c\xfb\x03\x84\x90\x28\x25\x39\x7f\xfe\x3c" \ | |||
|         "\x12\x28\x7e\x8a\x02\x02\xd8\xdd\xfd\x9a\xbd\xbd\x7d\x96\x16\x7b" \ | |||
|         "\x84\xbe\x87\x46\x3e\xe9\x03\x9a\xfb\xf7\xf6\x88\x93\x84\x38\x76" \ | |||
|         "\x28\x72\xa8\x56\x5d\xe6\xe7\x3b\x14\x79\x86\xd6\x70\xee\xdc\xf3" \ | |||
|         "\x5c\xba\x74\x89\xe3\x7a\xfd\x89\x16\x5c\xbb\xf6\x25\xfd\x7e\x9f" \ | |||
|         "\xb9\x46\x0d\x29\x25\xed\x76\x0b\x06\x23\x9a\xed\x16\x9e\x17\x81" \ | |||
|         "\x00\x43\x29\x02\xcf\x47\x08\xf8\xd7\xed\x7f\x13\xc7\x09\x8d\xb9" \ | |||
|         "\x3a\xa7\x37\x37\x58\x5e\x59\x39\x32\x7e\x27\x02\x08\x01\x5e\x10" \ | |||
|         "\x70\xfd\xc6\x4d\x2a\x8e\x4d\x96\x66\x24\x49\x42\x9e\x17\x44\x51" \ | |||
|         "\x8c\x32\x14\x68\x58\x5d\x5d\x24\x4f\x13\xa4\x94\xd4\xea\xd5\x43" \ | |||
|         "\xf9\xa5\xa4\x2c\x33\x36\xd6\x9f\xc3\x32\x8c\x23\xe3\x77\x22\x80" \ | |||
|         "\x7c\x22\xff\xd5\xcf\xae\xd2\xeb\x75\x48\xd3\x0c\xcb\xb2\x48\x93" \ | |||
|         "\x84\x2c\xcb\x49\xa2\x98\x30\x88\x88\xa3\x94\xb2\x48\x31\x0c\x03" \ | |||
|         "\x77\xbe\x45\xde\xa8\xe3\xe4\x05\xa7\xd6\x7a\x5c\xbe\x72\xe5\x24" \ | |||
|         "\x81\x8f\xb7\xe0\xe6\xcd\x5b\x3c\x7c\xf0\x08\x29\x0c\x0a\xad\xe9" \ | |||
|         "\x74\x5a\x20\x24\xcb\x2b\x8b\xa4\x69\x8a\x65\xd9\x28\x43\x31\x1a" \ | |||
|         "\x44\x80\xe4\xe1\x83\x3d\x82\x20\xa2\x5a\xaf\xb2\xbd\xf5\x02\xdb" \ | |||
|         "\x2f\xbe\xf8\xcc\xf8\x9d\x08\x90\xe5\x05\x37\x6f\xdc\xa2\xd1\x68" \ | |||
|         "\x50\x6f\xcc\xb1\xbf\xdf\x27\x08\x62\xa2\x30\xc4\x77\x2c\xb4\x2e" \ | |||
|         "\x69\xb7\x9b\x74\xe6\xbb\xa4\x69\x4a\xaf\xd7\x65\xd0\x1f\x20\xa4" \ | |||
|         "\x40\x88\x92\x53\xa7\x56\xa9\xd7\x6a\xc7\xfa\xff\x4c\x00\x29\xe0" \ | |||
|         "\xde\xc3\x87\x7c\xf0\xc1\x5f\x08\xc2\x14\x43\x05\x74\x3b\x73\x18" \ | |||
|         "\x96\xc3\x74\x3c\x01\x34\xa3\xe1\x98\xd1\x70\x42\x7f\x30\xa1\x2c" \ | |||
|         "\x72\xb2\x34\xc3\xa9\xb8\x28\x43\xd1\xee\x34\xb8\x70\xe1\x02\xe2" \ | |||
|         "\x44\x03\x9e\x01\x70\x18\xbf\x5d\x06\xc3\x21\x8e\x63\x33\x19\x4f" \ | |||
|         "\xb0\x2c\x93\x42\x2b\x1a\xcd\x26\xad\xa6\x4b\x51\xe4\xd4\x1b\x75" \ | |||
|         "\x1e\xef\x1d\x00\x30\x1a\x4d\xf0\xbd\x90\x8a\x6b\xb1\xbe\x73\x96" \ | |||
|         "\x57\x2f\x5e\x64\x96\x51\xeb\x48\x00\x0d\x7c\x7e\xed\x4b\x26\x93" \ | |||
|         "\x29\xbd\x5e\x97\x40\x81\x53\xa9\xf0\xf8\xf1\x90\xf1\x68\xcc\xb0" \ | |||
|         "\xbf\x8f\x69\x2a\x5c\xd7\xa5\x5a\xab\xd2\xed\xb6\xf1\xfd\x10\xdf" \ | |||
|         "\x0f\x50\x0a\xd6\x7f\x76\x8a\x85\x85\xc5\x13\xe5\x87\x23\x46\xb2" \ | |||
|         "\xc3\xdb\xcf\xe7\xda\xb5\x6b\x64\x69\x42\x7f\xbf\x4f\x92\xa4\xb8" \ | |||
|         "\xd5\x0a\xcd\x66\x8d\x85\xc5\x0e\x08\x41\xa9\x05\x77\xff\x7b\x9f" \ | |||
|         "\xc9\xd8\xc3\x0f\x42\x84\x28\x69\xb6\x1b\x74\xba\x2d\x5e\xd8\xda" \ | |||
|         "\xc2\x54\xb3\x4d\x7b\xf2\xa8\x1f\xbb\xbb\xbb\xdc\xb8\x79\x8b\xc5" \ | |||
|         "\xa5\x1e\xae\xeb\xa0\x81\x7b\x77\x1f\xe2\xf9\x01\xe8\x82\x7a\xdd" \ | |||
|         "\xa5\x3b\xdf\xc2\xb4\x14\x52\x68\xa6\xe3\x31\x7b\x8f\x1e\x13\xf8" \ | |||
|         "\x01\xed\x76\x8b\x2b\x33\xc4\xef\x58\x0b\xbe\xfa\xea\x16\x77\xee" \ | |||
|         "\x3c\xc2\x75\x1d\x2c\xd3\xa0\xd9\x6e\xe2\xfb\x11\x59\x96\xb1\xbf" \ | |||
|         "\xb7\x4f\x9e\x17\x28\x29\x31\x2d\x83\xd5\xce\x12\x42\x1c\xaa\xa1" \ | |||
|         "\x94\xe2\xf4\xe6\x3a\xa7\x9f\x7f\xfe\xc4\xf8\x3d\x13\x20\xcd\x0b" \ | |||
|         "\xae\xdf\xb8\x49\xad\xd1\xc0\x50\x8a\xe9\x64\x42\x56\x68\xa4\x54" \ | |||
|         "\x74\x3b\x4d\x3c\xcf\x27\x89\x13\xc2\x20\x24\x0c\x02\x04\x1a\xcb" \ | |||
|         "\xb6\x69\x34\x1b\x54\xaa\x15\x4e\x6f\x6e\x50\x75\xdd\x99\xfc\xff" \ | |||
|         "\x11\x80\x14\x70\xe7\xfe\x3d\x3e\xfa\xf0\xcf\x58\xa6\xa2\x56\xab" \ | |||
|         "\x61\x59\x26\x69\x9a\x32\x9d\x78\xa0\x0b\x40\xb3\xb4\xbc\x80\xe7" \ | |||
|         "\x79\x24\x69\x4c\xe0\x07\x8c\xfa\x63\x84\x61\xd0\x68\xb8\x9c\x39" \ | |||
|         "\x7b\x96\xa2\xd4\x08\x31\x4b\x08\x7f\x00\x20\x80\x7b\xf7\xef\x93" \ | |||
|         "\xc4\x01\xa1\x37\xc1\x9f\x4e\x59\x5a\x5e\xc2\xb2\x4c\x2c\xcb\x24" \ | |||
|         "\x8e\x22\xbc\xa9\x47\x59\x14\x08\x43\x31\xbf\xd0\xc5\x75\x1d\xee" \ | |||
|         "\xfc\xe7\x2e\x52\xc1\xea\xca\x12\x4b\xcb\x2b\x0c\x87\x23\xea\xf5" \ | |||
|         "\xfa\x93\x77\xc1\xf1\x4b\xfd\xfa\x37\xef\xbc\xf3\x34\x41\xbd\x56" \ | |||
|         "\x67\x71\xb9\xc7\x78\x32\xe2\xe0\xf1\x3e\x81\x17\xe0\x79\x3e\xdd" \ | |||
|         "\x6e\x07\xc3\x30\x10\x42\x20\xa4\xc4\x9b\x7a\x68\x01\xba\x28\x91" \ | |||
|         "\x4a\xd2\x6c\xd6\x79\xe9\xa5\x2d\x76\x76\x76\xc8\x8b\x02\x21\x04" \ | |||
|         "\xa6\x69\xa0\x94\x3a\x56\x8d\xef\x03\x00\xae\x5b\xe1\xdc\xd9\xb3" \ | |||
|         "\xbc\xfc\xca\xcb\x38\xae\xcd\x64\x32\x66\xd0\x1f\x10\xf8\x3e\x41" \ | |||
|         "\x10\xd2\x5b\xe8\x61\x3b\x0e\x49\x92\x52\x16\x05\xde\xd4\x23\x4d" \ | |||
|         "\x12\x2c\x5b\xf1\xfa\x6b\x97\xd8\xd8\xd8\x20\x4b\x53\xf2\x3c\x07" \ | |||
|         "\x0e\x21\xbe\x03\x9f\x09\xe0\x50\x09\xc1\x42\xaf\xc7\xc5\x8b\x17" \ | |||
|         "\x59\xdf\x5c\x47\x8b\x92\xc9\x74\xc2\x68\x30\x22\x89\x53\xa2\x30" \ | |||
|         "\xa6\x56\xab\xd2\x9d\xef\x12\xf8\x11\x5a\xc3\xf2\x52\x8f\x5f\xbe" \ | |||
|         "\xf9\x26\x8d\x46\x9d\xa2\x28\xc8\xb2\x9c\x2c\xcf\x0f\x7d\x36\x0c" \ | |||
|         "\x0c\xc3\x3c\x12\xe2\x68\x00\x0e\xbb\xa1\x65\x59\x9c\xde\xdc\xe4" \ | |||
|         "\xc2\x2b\xaf\xd0\x6c\xcd\x31\x1a\x8f\x89\xe3\x98\xe9\x68\x8c\x2e" \ | |||
|         "\x0b\xb4\x06\xa9\x14\xcd\x56\x93\xed\xad\x4d\xde\x78\xe3\x17\x48" \ | |||
|         "\xa9\x00\x8d\x2e\x4b\x8a\x22\x27\xcf\x0f\x5f\x47\x87\x10\x06\x52" \ | |||
|         "\x7e\x1f\xe2\xd8\xeb\xf8\xbb\x41\x72\x75\x75\x95\x5f\xbd\xfd\x36" \ | |||
|         "\xaf\xee\xec\xf0\xa7\xf7\xdf\xe7\xdd\x3f\xfc\x91\x83\xfd\x7d\xa6" \ | |||
|         "\xe3\x11\x85\x96\xd8\x8e\xc9\xd2\xe2\x02\x8e\xe3\xa0\xff\x3f\x7d" \ | |||
|         "\x6a\x8a\x3c\x27\x0c\x43\x84\x10\x68\x0d\x5a\x6b\xaa\xd5\x2a\xea" \ | |||
|         "\xa9\x2e\x39\xd3\x58\xae\x35\x98\x86\xc9\xab\x3b\x3b\xac\x9d\x3a" \ | |||
|         "\xc5\xc6\xfa\x3a\xef\xbe\xf7\x1e\x7f\xff\xeb\xa7\xe4\x71\x46\x99" \ | |||
|         "\xe7\xac\xaf\xaf\xa3\x94\xa2\x2c\xf5\x93\x2b\x59\x22\xe5\xe1\xa7" \ | |||
|         "\x35\x94\x65\x49\x59\x16\x68\x5d\xf2\x74\x03\xfe\x1f\xc2\x60\x72" \ | |||
|         "\xe2\x6a\x9b\x4e\x8f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60" \ | |||
|         "\x82" | |||
| @ -0,0 +1,79 @@ | |||
| function toggle(id, hide) { | |||
|     var elm = document.getElementById(id); | |||
|     if(hide) { | |||
|         if(!elm.classList.contains("hide")) | |||
|             elm.classList.add("hide"); | |||
|     } | |||
|     else | |||
|         elm.classList.remove('hide'); | |||
| } | |||
| 
 | |||
| function getAjax(url, ptr, method="GET", json=null) { | |||
|     var xhr = new XMLHttpRequest(); | |||
|     if(xhr != null) { | |||
|        xhr.open(method, url, true); | |||
|        xhr.onreadystatechange = p; | |||
|        if("POST" == method) | |||
|             xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); | |||
|        xhr.send(json); | |||
|     } | |||
|     function p() { | |||
|         if(xhr.readyState == 4) { | |||
|             if(null != xhr.responseText) | |||
|                 ptr(JSON.parse(xhr.responseText)); | |||
|         } | |||
|     } | |||
| } | |||
| 
 | |||
| function des(val) { | |||
|     e = document.createElement('p'); | |||
|     e.classList.add("subdes"); | |||
|     e.innerHTML = val; | |||
|     return e; | |||
| } | |||
| 
 | |||
| function lbl(htmlfor, val, cl=null, id=null) { | |||
|     e = document.createElement('label'); | |||
|     e.htmlFor = htmlfor; | |||
|     e.innerHTML = val; | |||
|     if(null != cl) e.classList.add(...cl); | |||
|     if(null != id) e.id = id; | |||
|     return e; | |||
| } | |||
| 
 | |||
| function inp(name, val, max=32, cl=["text"], id=null) { | |||
|     e = document.createElement('input'); | |||
|     e.classList.add(...cl); | |||
|     e.name = name; | |||
|     e.value = val; | |||
|     e.maxLength = max; | |||
|     if(null != id) e.id = id; | |||
|     return e; | |||
| } | |||
| 
 | |||
| function sel(name, opt, selId) { | |||
|     e = document.createElement('select'); | |||
|     e.name = name; | |||
|     for(it of opt) { | |||
|         o = document.createElement('option'); | |||
|         o.value = it[0]; | |||
|         o.innerHTML = it[1]; | |||
|         if(it[0] == selId) | |||
|             o.selected = true; | |||
|         e.appendChild(o); | |||
|     } | |||
|     return e; | |||
| } | |||
| 
 | |||
| function div(cl) { | |||
|     e = document.createElement('div'); | |||
|     e.classList.add(...cl); | |||
|     return e; | |||
| } | |||
| 
 | |||
| function span(val, cl) { | |||
|     e = document.createElement('span'); | |||
|     e.innerHTML = val; | |||
|     e.classList.add(...cl); | |||
|     return e; | |||
| } | |||
| @ -0,0 +1,100 @@ | |||
| #ifndef __FAVICON_ICO_GZ_H__ | |||
| #define __FAVICON_ICO_GZ_H__ | |||
| #define favicon_ico_gz_len 1533 | |||
| const uint8_t favicon_ico_gz[] PROGMEM = {0x1f, 0x8b, 0x08, 0x08, 0xf2, 0xc5, 0xd5, 0x62, 0x04, 0x00, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, | |||
| 0x6e, 0x2e, 0x69, 0x63, 0x6f, 0x00, 0xed, 0x5c, 0x49, 0x68, 0x13, 0x51, 0x18, 0xfe, 0x62, 0xa3, | |||
| 0x51, 0x28, 0xd6, 0x83, 0x82, 0xa0, 0x98, 0xb8, 0x1c, 0xbc, 0x59, 0x11, 0x5c, 0x50, 0xac, 0x88, | |||
| 0x8a, 0xb8, 0xdd, 0x3c, 0x89, 0xd0, 0x93, 0x7a, 0x53, 0x51, 0x9b, 0x80, 0x4b, 0x46, 0xad, 0xfb, | |||
| 0xd2, 0xb4, 0x2e, 0xb8, 0xa3, 0xc6, 0xba, 0xe1, 0x02, 0xae, 0xad, 0x0a, 0x26, 0x3d, 0xe8, 0xc5, | |||
| 0x83, 0x57, 0x31, 0x2d, 0xc1, 0x8b, 0xb7, 0x92, 0x63, 0x0e, 0xa1, 0xcf, 0xff, 0xcf, 0xbc, 0xc9, | |||
| 0x32, 0xa4, 0x66, 0xcf, 0x4b, 0xf3, 0xfa, 0xc3, 0xc7, 0x97, 0xcc, 0xcc, 0xcb, 0xf7, 0xbe, 0x6f, | |||
| 0x26, 0x6f, 0x26, 0xf3, 0x92, 0x00, 0x0e, 0x34, 0x61, 0xda, 0x34, 0x66, 0x0f, 0xf6, 0x38, 0x81, | |||
| 0xa5, 0x00, 0x3c, 0x1e, 0xf3, 0xf9, 0x53, 0x5a, 0x7e, 0x8f, 0x96, 0xad, 0x59, 0x63, 0x3e, 0x5f, | |||
| 0xb8, 0x16, 0xd8, 0x30, 0x03, 0x58, 0x48, 0xdb, 0xd0, 0x2a, 0x5a, 0x62, 0x2e, 0x1f, 0xad, 0x06, | |||
| 0xc3, 0x5d, 0xc6, 0x60, 0x38, 0x20, 0x08, 0x5e, 0xf1, 0x08, 0x77, 0x09, 0x42, 0xf4, 0xe2, 0xd7, | |||
| 0x67, 0x2f, 0x56, 0xf5, 0x7b, 0x21, 0x18, 0xfc, 0xb8, 0x63, 0x0b, 0x7e, 0x11, 0x84, 0x77, 0x0b, | |||
| 0xee, 0x2e, 0x9a, 0x03, 0x6f, 0xab, 0x1b, 0x82, 0x31, 0x38, 0x10, 0x38, 0xce, 0xaf, 0x51, 0x72, | |||
| 0x7b, 0xd2, 0xe6, 0xf6, 0xbf, 0xbf, 0xf6, 0xcc, 0x2e, 0xbd, 0x7d, 0xf2, 0x35, 0x76, 0x88, 0x20, | |||
| 0xf6, 0x96, 0xde, 0x3e, 0xe0, 0xa5, 0x76, 0xad, 0xd4, 0xfe, 0x64, 0xc9, 0xed, 0xa9, 0xff, 0xd4, | |||
| 0xd6, 0x4f, 0x68, 0x2f, 0xb9, 0x3d, 0xf5, 0x9f, 0x70, 0xb8, 0xaf, 0x03, 0x4b, 0x4b, 0x6e, 0x4f, | |||
| 0xfd, 0xe7, 0x7d, 0x4b, 0x6d, 0x4f, 0x96, 0xd3, 0xde, 0x7a, 0x8d, 0x72, 0xda, 0x73, 0x7d, 0xdb, | |||
| 0x8f, 0x29, 0x45, 0xb5, 0x7f, 0x84, 0x4e, 0xfb, 0x71, 0xda, 0xb1, 0x19, 0x9d, 0x05, 0xb5, 0xef, | |||
| 0xc5, 0xa5, 0xd1, 0x8e, 0x75, 0xef, 0x56, 0x9c, 0xfa, 0x6f, 0xfb, 0x87, 0x58, 0x86, 0x3c, 0x75, | |||
| 0x70, 0x13, 0x96, 0xe5, 0x6a, 0x1f, 0x19, 0xe8, 0x6a, 0x47, 0x81, 0xd5, 0xea, 0x41, 0x7b, 0x56, | |||
| 0xfb, 0x50, 0xe0, 0x72, 0xae, 0xed, 0xbe, 0x1a, 0x98, 0xcc, 0xc8, 0xf9, 0x1a, 0x73, 0x70, 0x9a, | |||
| 0xda, 0x8f, 0xda, 0xf6, 0xb9, 0x81, 0x49, 0xfd, 0x3e, 0xbc, 0xa5, 0xfd, 0xd7, 0xcf, 0xf9, 0x8f, | |||
| 0xf6, 0x1a, 0xff, 0xeb, 0xe7, 0x04, 0x42, 0x13, 0xc3, 0x20, 0x84, 0x00, 0x57, 0x14, 0x68, 0x89, | |||
| 0x01, 0xee, 0x38, 0xb0, 0xfa, 0x08, 0x70, 0x64, 0xb5, 0x39, 0xce, 0x78, 0x08, 0x6b, 0x0a, 0x1f, | |||
| 0x67, 0x2c, 0x0c, 0xf3, 0x72, 0xf1, 0x18, 0xab, 0xe4, 0x3e, 0x4f, 0x23, 0x88, 0x37, 0xbc, 0xae, | |||
| 0xcf, 0x07, 0xaf, 0xf9, 0x1e, 0x48, 0x83, 0x97, 0xf1, 0xba, 0x43, 0x5b, 0xf0, 0x86, 0x8f, 0x87, | |||
| 0x4c, 0xd0, 0xfe, 0x5d, 0xc5, 0xeb, 0x28, 0x97, 0x61, 0x33, 0xdf, 0x34, 0x78, 0x39, 0x8f, 0x55, | |||
| 0xaa, 0xf4, 0x23, 0x03, 0x81, 0x53, 0x56, 0x1f, 0x54, 0xe8, 0xb3, 0xe6, 0x50, 0x28, 0x70, 0xde, | |||
| 0xea, 0x83, 0x0a, 0xfd, 0x64, 0x1f, 0xc2, 0x81, 0x93, 0xbc, 0x0d, 0xf1, 0x01, 0x15, 0xfa, 0x49, | |||
| 0x0c, 0x74, 0x9d, 0xe3, 0xed, 0x94, 0xe9, 0x13, 0x78, 0x1f, 0x24, 0xf5, 0x7b, 0xb1, 0x54, 0x89, | |||
| 0x3e, 0xef, 0x7f, 0x59, 0x7c, 0xce, 0x53, 0xa5, 0x2f, 0x7a, 0xe0, 0x92, 0x7d, 0x38, 0xad, 0x44, | |||
| 0x9f, 0xf6, 0x3f, 0x9f, 0xb3, 0x65, 0x1f, 0x3a, 0x95, 0xe8, 0x9b, 0xbe, 0x8f, 0x59, 0x7d, 0x50, | |||
| 0xa4, 0x9f, 0xea, 0x03, 0xe9, 0xaf, 0x57, 0xa4, 0x9f, 0xea, 0x83, 0x42, 0x7d, 0xbe, 0x7e, 0xd9, | |||
| 0xcf, 0xeb, 0x3e, 0xf9, 0xd0, 0xae, 0x44, 0x9f, 0xf7, 0xbf, 0x2c, 0x3a, 0x87, 0x9f, 0x56, 0xa9, | |||
| 0x6f, 0xf5, 0x41, 0xa5, 0xbe, 0xbc, 0x0e, 0x3d, 0xa3, 0x52, 0xdf, 0xea, 0x83, 0x4a, 0x7d, 0xae, | |||
| 0x0f, 0x87, 0x30, 0x5f, 0xa5, 0x3e, 0x57, 0x0d, 0xf4, 0x8d, 0x7c, 0xfa, 0xa4, 0x67, 0x54, 0x49, | |||
| 0xff, 0x86, 0x10, 0x74, 0x29, 0x9b, 0xbf, 0x1c, 0xa4, 0xd9, 0x5d, 0x51, 0xfd, 0x20, 0x6e, 0x16, | |||
| 0xa8, 0x9d, 0xd5, 0x87, 0x8a, 0xe8, 0x17, 0xaf, 0x9d, 0xea, 0x03, 0xe9, 0xf6, 0x94, 0xa9, 0x7f, | |||
| 0xab, 0x44, 0xed, 0xac, 0x3e, 0x94, 0xa4, 0xff, 0x1c, 0xcd, 0x65, 0x6a, 0xa7, 0xfa, 0x60, 0x6c, | |||
| 0x47, 0x73, 0xb1, 0xfa, 0xd5, 0xa8, 0x42, 0xf4, 0x23, 0xe1, 0xc0, 0x15, 0x54, 0xa9, 0x16, 0xbb, | |||
| 0x71, 0xe5, 0x7f, 0xfa, 0x91, 0x50, 0xf7, 0x6d, 0x21, 0x8c, 0x09, 0xa8, 0x5e, 0x39, 0x48, 0xf3, | |||
| 0x6a, 0x86, 0x7e, 0x59, 0xda, 0xfd, 0x07, 0x31, 0x97, 0x81, 0xe2, 0x2a, 0xdd, 0x87, 0xb4, 0xfe, | |||
| 0x9d, 0x62, 0xb5, 0xe9, 0xde, 0xcb, 0x1c, 0x3a, 0xef, 0x0c, 0xf5, 0x77, 0xe0, 0xcf, 0x17, 0x1f, | |||
| 0xe6, 0xa1, 0xb8, 0xe2, 0x3e, 0x5c, 0x23, 0x94, 0xa4, 0xfd, 0xd1, 0x07, 0x0f, 0x5d, 0x03, 0x45, | |||
| 0xad, 0x73, 0x3f, 0x3f, 0xe6, 0x65, 0x28, 0xae, 0x92, 0x7d, 0x70, 0x4e, 0x9e, 0xe6, 0xe9, 0x24, | |||
| 0xac, 0x23, 0x2c, 0x20, 0x4c, 0x27, 0x34, 0x13, 0x78, 0xf9, 0x24, 0xc2, 0x44, 0x7e, 0x7c, 0x96, | |||
| 0xb0, 0x91, 0xb0, 0x90, 0x30, 0xd3, 0x5c, 0xe7, 0x6c, 0x26, 0x4c, 0x25, 0xb4, 0x30, 0x02, 0x84, | |||
| 0x9f, 0x84, 0x58, 0x1a, 0xee, 0xb8, 0xc7, 0xd9, 0x96, 0xf0, 0x38, 0xfd, 0x23, 0x1e, 0xa7, 0x10, | |||
| 0x1e, 0x97, 0x10, 0x51, 0x0b, 0x2d, 0x23, 0xfe, 0x98, 0x3b, 0xd1, 0x16, 0x6f, 0x8b, 0xbb, 0x13, | |||
| 0x0b, 0xe5, 0x3d, 0x0a, 0xa3, 0xf8, 0xfb, 0x14, 0xb9, 0x3e, 0xbb, 0x45, 0xac, 0xed, 0xe5, 0x67, | |||
| 0x38, 0x2f, 0x8f, 0xa1, 0x79, 0xd1, 0x8b, 0x5d, 0xb6, 0xeb, 0x89, 0x61, 0xce, 0x35, 0x0f, 0x86, | |||
| 0x6d, 0xf7, 0xc1, 0x76, 0xf1, 0x58, 0x9b, 0x17, 0x9b, 0xe1, 0xb5, 0x8d, 0x09, 0x91, 0xf4, 0xfb, | |||
| 0x21, 0x8d, 0x02, 0xee, 0x93, 0x68, 0xe7, 0x3f, 0x6f, 0x06, 0x1a, 0xf8, 0xe7, 0x1a, 0x0a, 0x75, | |||
| 0x9d, 0xd0, 0xcd, 0xff, 0xe0, 0x40, 0xf7, 0xa5, 0xbc, 0x19, 0x34, 0xb2, 0x7f, 0xf3, 0xda, 0xe0, | |||
| 0xa2, 0x3d, 0x03, 0x9d, 0xfc, 0xe7, 0xca, 0x80, 0xef, 0x5b, 0xea, 0xe4, 0xdf, 0xcc, 0xa0, 0xeb, | |||
| 0x82, 0x3d, 0x03, 0x9d, 0xfc, 0xe7, 0xca, 0x20, 0x12, 0xee, 0xee, 0xd4, 0xc9, 0x3f, 0x21, 0x35, | |||
| 0x77, 0x91, 0xce, 0x20, 0xb0, 0x5b, 0x27, 0xff, 0xe9, 0xb9, 0x93, 0x74, 0xe9, 0xe6, 0xdf, 0xca, | |||
| 0x40, 0x67, 0xff, 0xd6, 0xdc, 0x91, 0xd6, 0xfe, 0x93, 0xe8, 0xf6, 0xdb, 0xfc, 0xb7, 0xe7, 0xf5, | |||
| 0xde, 0x48, 0xfe, 0x6d, 0xe3, 0xbf, 0x35, 0x7f, 0xa5, 0xb3, 0xff, 0x82, 0x32, 0x68, 0x70, 0xff, | |||
| 0x32, 0x83, 0x33, 0xba, 0xf9, 0x17, 0x0f, 0x30, 0x2b, 0xeb, 0x79, 0x10, 0x97, 0xb5, 0xf2, 0x4f, | |||
| 0xe3, 0x3f, 0xc1, 0x6f, 0xcb, 0xe0, 0xac, 0x56, 0xfe, 0xe5, 0xdc, 0xad, 0x3d, 0x03, 0xcd, 0xfc, | |||
| 0x67, 0x65, 0xc0, 0xf3, 0x0a, 0xf4, 0xbc, 0x5b, 0x33, 0xff, 0x3c, 0x97, 0x74, 0xd4, 0x9e, 0x81, | |||
| 0x56, 0xfe, 0x73, 0x67, 0xd0, 0xa3, 0x95, 0x7f, 0x13, 0x87, 0x6d, 0x19, 0xcc, 0xd6, 0xcc, 0x3f, | |||
| 0x1f, 0xf3, 0x47, 0x32, 0xb7, 0xd5, 0xce, 0xbf, 0x3c, 0x0e, 0x34, 0xf7, 0x9f, 0xcc, 0x40, 0x73, | |||
| 0xff, 0x3c, 0x26, 0x6e, 0xb3, 0xda, 0xc8, 0xef, 0x57, 0x5d, 0xd1, 0xca, 0xbf, 0x6d, 0xfc, 0x17, | |||
| 0x34, 0x15, 0xd9, 0xe7, 0xc5, 0x55, 0x5d, 0xfd, 0x17, 0x98, 0x41, 0x43, 0xfb, 0xb7, 0x32, 0xa0, | |||
| 0xef, 0xd8, 0x5d, 0xd3, 0xd5, 0x7f, 0x66, 0x06, 0xba, 0xfa, 0xcf, 0xcc, 0x40, 0x57, 0xff, 0x56, | |||
| 0x06, 0xe4, 0xf9, 0xba, 0xae, 0xfe, 0x33, 0x33, 0xd0, 0xd5, 0x7f, 0x66, 0x06, 0xba, 0xfa, 0xcf, | |||
| 0x18, 0x0f, 0x4e, 0xeb, 0xea, 0xdf, 0xaa, 0x71, 0xff, 0xe3, 0xfe, 0x1b, 0xc4, 0xff, 0x4e, 0x94, | |||
| 0x59, 0x1d, 0x5b, 0xb1, 0x33, 0xaf, 0xf7, 0x7a, 0xf4, 0x1f, 0xc4, 0x13, 0xf1, 0x9c, 0x7e, 0x4e, | |||
| 0x5a, 0x66, 0x19, 0x06, 0x26, 0x50, 0x06, 0xf7, 0xc7, 0x94, 0xff, 0x0a, 0x79, 0x2f, 0x2a, 0x83, | |||
| 0xfa, 0xf1, 0xff, 0x54, 0x7c, 0x85, 0x13, 0x15, 0x2e, 0xce, 0x80, 0x7e, 0x83, 0xf2, 0xa0, 0xce, | |||
| 0xfd, 0x57, 0xc5, 0xbb, 0x55, 0xdb, 0xb7, 0xa3, 0x89, 0x33, 0xa8, 0x53, 0xff, 0xcf, 0xaa, 0xe9, | |||
| 0x3d, 0x33, 0x03, 0xf2, 0xfb, 0xb0, 0xce, 0xfc, 0xd7, 0xc4, 0xbb, 0x3d, 0x83, 0x3a, 0xf1, 0x5f, | |||
| 0x53, 0xef, 0x59, 0xef, 0x85, 0xad, 0x08, 0x2a, 0xf6, 0xff, 0x5c, 0x85, 0x77, 0x7b, 0x06, 0x8a, | |||
| 0xfc, 0x2b, 0xf5, 0x6e, 0xcf, 0xa0, 0xa6, 0xfe, 0x83, 0x58, 0x54, 0xc9, 0xf3, 0x7b, 0xb9, 0xc5, | |||
| 0x19, 0xd0, 0xff, 0x9c, 0x2c, 0xaa, 0x95, 0xff, 0xb1, 0x50, 0xe3, 0xfe, 0xc7, 0xfd, 0xb7, 0x56, | |||
| 0xc8, 0xff, 0x50, 0xb8, 0xeb, 0x3e, 0xc6, 0x58, 0xd1, 0x7f, 0xfd, 0xdc, 0x6f, 0xad, 0x8c, 0xff, | |||
| 0x17, 0x3f, 0x7e, 0xdc, 0x9c, 0x88, 0x31, 0x56, 0xdb, 0xe9, 0x2f, 0x8c, 0xc8, 0x5f, 0x6f, 0x99, | |||
| 0xfe, 0xc7, 0xa4, 0xf7, 0x62, 0x32, 0x68, 0x54, 0xef, 0xb6, 0x0c, 0x1e, 0x17, 0xe9, 0xff, 0x65, | |||
| 0x23, 0x78, 0x2f, 0x24, 0x03, 0x95, 0xde, 0x69, 0x1e, 0xf8, 0x14, 0x03, 0x35, 0x28, 0xce, 0x60, | |||
| 0x91, 0x1b, 0x4f, 0x6c, 0xfe, 0xd5, 0x79, 0xf7, 0xe1, 0x44, 0x6a, 0xce, 0xab, 0x03, 0xe7, 0x51, | |||
| 0x83, 0xca, 0x91, 0x41, 0xe6, 0x77, 0xfc, 0xdf, 0xfd, 0xfe, 0xd0, 0xe3, 0x42, 0x0d, 0xea, 0x93, | |||
| 0x17, 0xc7, 0xed, 0xf3, 0xbe, 0x74, 0x1c, 0x9c, 0x43, 0x0d, 0x6a, 0xc9, 0x12, 0x4c, 0x24, 0xdf, | |||
| 0xaf, 0x6c, 0xfe, 0xdf, 0xab, 0xf4, 0xae, 0x28, 0x83, 0xd7, 0x84, 0xba, 0xf1, 0x6e, 0x81, 0xb7, | |||
| 0x41, 0x0d, 0xca, 0xca, 0x00, 0xcb, 0x11, 0x75, 0xd1, 0xd7, 0x14, 0x0d, 0xc9, 0x21, 0xc9, 0x51, | |||
| 0xc9, 0xfb, 0x24, 0x2f, 0x97, 0x3c, 0x5b, 0xf2, 0x54, 0xc9, 0x2e, 0xc9, 0x4d, 0x7d, 0x26, 0x3b, | |||
| 0xe2, 0x26, 0xc3, 0x62, 0x9f, 0xe4, 0xe5, 0x92, 0x57, 0x49, 0x5e, 0x2d, 0xb9, 0x4d, 0xf2, 0x4a, | |||
| 0xc3, 0xe4, 0x15, 0x21, 0xb9, 0x7d, 0x54, 0x72, 0xbb, 0xe4, 0x56, 0xc9, 0x33, 0x25, 0x37, 0x4b, | |||
| 0x76, 0x49, 0x6e, 0x92, 0xec, 0xb0, 0xf4, 0xec, 0x1c, 0x93, 0x1c, 0x97, 0x9c, 0x90, 0x3c, 0x22, | |||
| 0x59, 0x58, 0x7c, 0x46, 0xf2, 0x77, 0xc9, 0x7f, 0x25, 0x8b, 0x82, 0xd8, 0x41, 0x7f, 0x3b, 0x91, | |||
| 0xec, 0x8f, 0x10, 0x21, 0x66, 0xfe, 0x67, 0x06, 0xe6, 0x16, 0x21, 0x62, 0xcc, 0x6e, 0x21, 0xe2, | |||
| 0xcc, 0x6d, 0x42, 0x24, 0x98, 0xfd, 0x42, 0x8c, 0x30, 0x0b, 0x2a, 0xf6, 0xcf, 0x7c, 0x87, 0x72, | |||
| 0x61, 0x4e, 0xe4, 0x67, 0x3f, 0xf3, 0x08, 0x7d, 0x28, 0x4f, 0x16, 0x1c, 0x26, 0x1b, 0x4d, 0xb4, | |||
| 0x90, 0x56, 0x85, 0x5c, 0xb4, 0x11, 0x6d, 0x1a, 0x6d, 0x21, 0x55, 0x52, 0x8e, 0x31, 0xf8, 0x31, | |||
| 0x2f, 0xe3, 0x75, 0xff, 0x00, 0xd3, 0x39, 0x74, 0x2c, 0x6e, 0x57, 0x00, 0x00}; | |||
| #endif /*__FAVICON_ICO_GZ_H__*/ | |||
| @ -1,72 +1,127 @@ | |||
| <!doctype html> | |||
| <html> | |||
|     <head> | |||
|         <title>Index - {DEVICE}</title> | |||
|         <title>Index</title> | |||
|         <link rel="stylesheet" type="text/css" href="style.css"/> | |||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
|         <script type="text/javascript"> | |||
|             getAjax('/uptime', 'uptime'); | |||
|             getAjax('/cmdstat', 'cmds'); | |||
|             window.setInterval("getAjax('/uptime', 'uptime')", {JS_TS}); | |||
|             window.setInterval("getAjax('/cmdstat', 'cmds')", {JS_TS}); | |||
| 
 | |||
|             function getAjax(url, resid) { | |||
|                 var http = null; | |||
|                 http = new XMLHttpRequest(); | |||
|                 if(http != null) { | |||
|                    http.open("GET", url, true); | |||
|                    http.onreadystatechange = print; | |||
|                    http.send(null); | |||
|                 } | |||
| 
 | |||
|                 function print() { | |||
|                     if(http.readyState == 4) { | |||
|                         document.getElementById(resid).innerHTML = http.responseText; | |||
|                     } | |||
|                 } | |||
|             } | |||
|             function getInverterInfo(data){ | |||
|                 var http = null; | |||
|                 http = new XMLHttpRequest(); | |||
|                 if(http != null) { | |||
|                    http.open("POST", "/api"); | |||
|                    http.setRequestHeader("Accept", "application/json"); | |||
|                    http.setRequestHeader("Content-Type", "application/json"); | |||
|                    http.send(data); | |||
|                 } | |||
|             } | |||
|         </script> | |||
|         <script type="text/javascript" src="api.js"></script> | |||
|     </head> | |||
|     <body> | |||
|         <h1>AHOY - {DEVICE}</h1> | |||
|         <h1>AHOY</h1> | |||
|         <div id="content" class="content"> | |||
|             <p> | |||
|                 <a href="/visualization">Visualization</a><br/> | |||
|                 <a href="/live">Visualization</a><br/> | |||
|                 <br/> | |||
|                 <a href="/setup">Setup</a><br/> | |||
|                 <a href="/serial">Webserial & Commands</a><br/> | |||
|             </p> | |||
|             <p><span class="des">Uptime: </span><span id="uptime"></span></p> | |||
|             <p><span class="des">Statistics: </span><pre id="cmds"></pre></p> | |||
|             <p>Every {TS}seconds the values are updated</p> | |||
|             <p><span class="des">ESP-Time: </span><span id="date"></span></p> | |||
|             <p><span class="des">RSSI: </span><span id="wifi_rssi"></span>dBm</p> | |||
|             <p> | |||
|                 <span class="des">Statistics: </span> | |||
|                 <pre id="stat"></pre> | |||
|                 <pre id="iv"></pre> | |||
|                 <pre id="warn_info"></pre> | |||
|             </p> | |||
|             <p>Every <span id="refresh"></span> seconds the values are updated</p> | |||
| 
 | |||
|             <div id="note"> | |||
|                 This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/> | |||
|                 New updates can be found on Github: <a href="https://github.com/grindylow/ahoy" target="_blank">https://github.com/grindylow/ahoy</a><br/> | |||
|                 New updates can be found on Github: <a href="https://github.com/lumapu/ahoy" target="_blank">https://github.com/lumapu/ahoy</a><br/> | |||
|                 <br/> | |||
|                 Please report issues using the feature provided by <a href="https://github.com/grindylow/ahoy/issues">Github</a><br/> | |||
|                 Please report issues using the feature provided by <a href="https://github.com/lumapu/ahoy/issues">Github</a><br/> | |||
|                 <br/> | |||
|                 Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a> | |||
|                 <br/> | |||
|                 <p class="lic"><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/> | |||
|                 Check the licenses which are published on <a href="https://github.com/grindylow/ahoy">https://github.com/grindylow/ahoy</a> as well</p> | |||
|                 Check the licenses which are published on <a href="https://github.com/lumapu/ahoy">https://github.com/lumapu/ahoy</a> as well</p> | |||
|             </div> | |||
|         </div> | |||
|         <div id="footer"> | |||
|             <p class="left">© 2022</p> | |||
|             <p class="left"><a href="/update">Update Firmware</a></p> | |||
|             <p class="right">AHOY :: {VERSION}</p> | |||
|             <p class="right" id="version"></p> | |||
|             <p class="right"><a href="/reboot">Reboot</a></p> | |||
|             <p class="right">Git SHA: {BUILD}</p> | |||
|             <p class="right"><a href="/api">REST API</a></p> | |||
|         </div> | |||
|         <script type="text/javascript"> | |||
|             var mIntervalSet = false; | |||
|             function parseSys(obj) { | |||
|                 document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; | |||
|                 document.getElementById("wifi_rssi").innerHTML = obj["wifi_rssi"];; | |||
| 
 | |||
|                 var date = new Date(obj["ts_now"] * 1000); | |||
|                 var up = obj["ts_uptime"]; | |||
|                 var days = parseInt(up / 86400) % 365; | |||
|                 var hrs  = parseInt(up / 3600) % 24; | |||
|                 var min  = parseInt(up / 60) % 60; | |||
|                 var sec  = up % 60; | |||
|                 document.getElementById("uptime").innerHTML = days + " Days, " | |||
|                     + ("0"+hrs).substr(-2) + ":" | |||
|                     + ("0"+min).substr(-2) + ":" | |||
|                     + ("0"+sec).substr(-2); | |||
|                 document.getElementById("date").innerHTML = date.toLocaleString('de-DE', {timeZone: 'UTC'}); | |||
|             } | |||
| 
 | |||
|             function parseStat(obj) { | |||
|                 document.getElementById("stat").innerHTML = "RX success: " + obj["rx_success"] | |||
|                     + "\nRX fail: " + obj["rx_fail"] | |||
|                     + "\nRX no anwser: " + obj["rx_fail_answer"] | |||
|                     + "\nFrames received: " + obj["frame_cnt"] | |||
|                     + "\nTX Cnt: " + obj["tx_cnt"]; | |||
|             } | |||
| 
 | |||
|             function parseIv(obj) { | |||
|                 var html = ""; | |||
|                 for(var i of obj) { | |||
|                     html += "Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is "; | |||
|                     if(false == i["is_avail"]) | |||
|                         html += "not "; | |||
|                     html += "available and is "; | |||
|                     if(false == i["is_producing"]) | |||
|                         html += "not "; | |||
|                     html += "producing\n"; | |||
| 
 | |||
|                     if(false == i["is_avail"]) { | |||
|                         if(i["ts_last_success"] > 0) { | |||
|                             var date = new Date(i["ts_last_success"] * 1000); | |||
|                             html += "-> last successful transmission: " + date.toLocaleString('de-DE', {timeZone: 'UTC'}); | |||
|                         } | |||
|                     } | |||
| 
 | |||
|                 } | |||
|                 document.getElementById("iv").innerHTML = html; | |||
|             } | |||
| 
 | |||
|             function parseWarnInfo(warn, info) { | |||
|                 var html = ""; | |||
|                 for(var w of warn) { | |||
|                     html += "WARN: " + w + "\n"; | |||
|                 } | |||
|                 for(var i of info) { | |||
|                     html += "INFO: " + i + "\n"; | |||
|                 } | |||
|                 document.getElementById("warn_info").innerHTML = html; | |||
|             } | |||
| 
 | |||
|             function parse(obj) { | |||
|                 if(null != obj) { | |||
|                     parseSys(obj["system"]); | |||
|                     parseStat(obj["statistics"]); | |||
|                     parseIv(obj["inverter"]); | |||
|                     parseWarnInfo(obj["warnings"], obj["infos"]); | |||
|                     document.getElementById("refresh").innerHTML = obj["refresh_interval"]; | |||
|                     if(false == mIntervalSet) { | |||
|                         window.setInterval("getAjax('/api/index', parse)", obj["refresh_interval"] * 1000); | |||
|                         mIntervalSet = true; | |||
|                     } | |||
|                 } | |||
|                 else | |||
|                     document.getElementById("refresh").innerHTML = "n/a"; | |||
|             } | |||
| 
 | |||
|             getAjax("/api/index", parse); | |||
|         </script> | |||
|     </body> | |||
| </html> | |||
|  | |||
| @ -0,0 +1,188 @@ | |||
| <!doctype html> | |||
| <html> | |||
|     <head> | |||
|         <title>Serial Console</title> | |||
|         <link rel="stylesheet" type="text/css" href="style.css"/> | |||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
|         <script type="text/javascript" src="api.js"></script> | |||
|     </head> | |||
|     <body> | |||
|         <h1>Serial Console</h1> | |||
|         <div id="content" class="content"> | |||
|             <div class="serial"> | |||
|                 <textarea id="serial" cols="80" rows="20" readonly></textarea><br/> | |||
|                 connected: <span class="dot" id="connected"></span>  | |||
|                 Uptime: <span id="uptime"></span> | |||
|                 <input type="button" value="clear" class="btn" id="clear"/>  | |||
|                 <input type="button" value="autoscroll" class="btn" id="scroll"/> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <hr> | |||
|                 <h3>Commands</h3> | |||
|                 <br/> | |||
|                 <label for="iv">Select Inverter:</label> | |||
|                 <select name="iv" id="InvID"> | |||
|                 </select> | |||
|                 <br/> | |||
|                 <div id="power"> | |||
|                     <input type="button" value="Restart" class="btn" id="restart"/> | |||
|                     <input type="button" value="Turn Off" class="btn" id="power_off"/> | |||
|                     <input type="button" value="Turn On" class="btn" id="power_on"/> | |||
|                 </div> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <br/> | |||
|                 <label>Send Power Limit: </label> | |||
|                 <input type="number" class="text" name="pwrlimval" maxlength="4"/> | |||
|                 <label> </label> | |||
|                 <select name="pwrlimcntrl" id="pwrlimcntrl"> | |||
|                     <option value="" selected disabled hidden>select the unit and persistence</option> | |||
|                     <option value="0">absolute in Watt non persistent</option> | |||
|                     <option value="1">relative in percent non persistent</option> | |||
|                     <option value="256">absolute in Watt persistent</option> | |||
|                     <option value="257">relative in percent persistent</option> | |||
|                 </select> | |||
|                 <br/> | |||
|                 <input type="button" value="Send Power Limit" class="btn" id="sendpwrlim"/> | |||
|                 <br/> | |||
|                 <p>Ctrl result: <span id="result">n/a</span></p> | |||
|             </div> | |||
|         </div> | |||
|         <div id="footer"> | |||
|             <p class="left">© 2022</p> | |||
|             <p class="left"><a href="/">Home</a></p> | |||
|             <p class="right" id="version"></p> | |||
|         </div> | |||
|         <script type="text/javascript"> | |||
|             var mAutoScroll = true; | |||
|             var con = document.getElementById("serial"); | |||
|             var mIntervalSet = false; | |||
| 
 | |||
|             function parseSys(obj) { | |||
|                 var up = obj["ts_uptime"]; | |||
|                 var days = parseInt(up / 86400) % 365; | |||
|                 var hrs  = parseInt(up / 3600) % 24; | |||
|                 var min  = parseInt(up / 60) % 60; | |||
|                 var sec  = up % 60; | |||
|                 document.getElementById("uptime").innerHTML = days + " Days, " | |||
|                     + ("0"+hrs).substr(-2) + ":" | |||
|                     + ("0"+min).substr(-2) + ":" | |||
|                     + ("0"+sec).substr(-2); | |||
| 
 | |||
|                 if(false == mIntervalSet) { | |||
|                     document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; | |||
|                     window.setInterval("getAjax('/api/system', parseSys)", 10000); | |||
|                     mIntervalSet = true; | |||
|                 } | |||
|             } | |||
| 
 | |||
|             function parse(root) { | |||
|                 select = document.getElementById('InvID'); | |||
| 
 | |||
|                 if(null == root) return; | |||
|                 root = root.inverter; | |||
|                 for(var i = 0; i < root.inverter.length; i++) | |||
|                 { | |||
|                     inv = root.inverter[i]; | |||
|                     var opt = document.createElement('option'); | |||
|                     opt.value = inv.id; | |||
|                     opt.innerHTML = inv.name; | |||
|                     select.appendChild(opt); | |||
|                 } | |||
|             } | |||
| 
 | |||
|             document.getElementById("clear").addEventListener("click", function() { | |||
|                 con.value = ""; | |||
|             }); | |||
|             document.getElementById("scroll").addEventListener("click", function() { | |||
|                 mAutoScroll = !mAutoScroll; | |||
|                 this.value = (mAutoScroll) ? "autoscroll" : "manual scoll"; | |||
|             }); | |||
| 
 | |||
|             if (!!window.EventSource) { | |||
|                 var source = new EventSource('/events'); | |||
|                 source.addEventListener('open', function(e) { | |||
|                     document.getElementById("connected").style.backgroundColor = "#0c0"; | |||
|                 }, false); | |||
| 
 | |||
|                 source.addEventListener('error', function(e) { | |||
|                     if (e.target.readyState != EventSource.OPEN) { | |||
|                         document.getElementById("connected").style.backgroundColor = "#f00"; | |||
|                     } | |||
|                 }, false); | |||
| 
 | |||
|                 source.addEventListener('serial', function(e) { | |||
|                     con.value += e.data.replace(/\<rn\>/g, '\r\n'); | |||
|                     if(mAutoScroll) | |||
|                         con.scrollTop = con.scrollHeight; | |||
|                 }, false); | |||
|             } | |||
| 
 | |||
|             getAjax("/api/system", parseSys); | |||
| 
 | |||
|             // only for test | |||
|             function ctrlCb(obj) { | |||
|                 var e = document.getElementById("result"); | |||
|                 if(obj["success"]) | |||
|                     e.innerHTML = "ok"; | |||
|                 else | |||
|                     e.innerHTML = "Error: " + obj["error"]; | |||
|             } | |||
| 
 | |||
|             function get_selected_iv() | |||
|             { | |||
|                 var e = document.getElementById("InvID"); | |||
|                 return  parseInt(e.value); | |||
|             } | |||
| 
 | |||
|             const wrapper = document.getElementById('power'); | |||
| 
 | |||
|             wrapper.addEventListener('click', (event) => { | |||
|                 var power = event.target.value; | |||
|                 var obj = new Object(); | |||
| 
 | |||
|                 switch (power) | |||
|                 { | |||
|                     case "Turn On": | |||
|                         obj.cmd = 0; | |||
|                         break; | |||
|                     case "Turn Off": | |||
|                         obj.cmd = 1; | |||
|                         break; | |||
|                     default: | |||
|                         obj.cmd = 2; | |||
|                 }             | |||
| 
 | |||
|                 obj.inverter = get_selected_iv(); | |||
|                 obj.tx_request = 81; | |||
|                 getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); | |||
|             }); | |||
| 
 | |||
|             document.getElementById("sendpwrlim").addEventListener("click", function() { | |||
|                 var val = parseInt(document.getElementsByName('pwrlimval')[0].value); | |||
|                 var ctrl = parseInt(document.getElementsByName('pwrlimcntrl')[0].value); | |||
|                  | |||
|                 if((ctrl == 1 || ctrl == 257) && unit < 2) unit = 2; | |||
|                 if(isNaN(val) || isNaN(ctrl))  | |||
|                 { | |||
|                     var tmp = (isNaN(val)) ? "Value" : "Unit"; | |||
|                     document.getElementById("result").textContent = tmp + " is missing"; | |||
|                     return; | |||
|                 } | |||
|                  | |||
|                 var obj = new Object(); | |||
|                 obj.inverter = get_selected_iv(); | |||
|                 obj.cmd = 11; | |||
|                 obj.tx_request = 81; | |||
|                 obj.payload = [val, ctrl]; | |||
|                 getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); | |||
|             }); | |||
| 
 | |||
|             getAjax("/api/setup", parse); | |||
|         </script> | |||
|     </body> | |||
| </html> | |||
| @ -0,0 +1,33 @@ | |||
| <!doctype html> | |||
| <html> | |||
|     <head> | |||
|         <title>Update</title> | |||
|         <link rel="stylesheet" type="text/css" href="style.css"/> | |||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
|         <script type="text/javascript" src="api.js"></script> | |||
|     </head> | |||
|     <body> | |||
|         <h1>Update</h1> | |||
|         <div id="content" class="content"> | |||
|             <div> | |||
|                 Make sure that you have noted all your settings before starting an update. New versions may have changed their memory layout which can break your existing settings. | |||
|             </div> | |||
|             <br/><br/> | |||
|             <form method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8"> | |||
|                 <input type="file" name="update"><input type="submit" value="Update"> | |||
|             </form> | |||
|         </div> | |||
|         <div id="footer"> | |||
|             <p class="left">© 2022</p> | |||
|             <p class="left"><a href="/">Home</a></p> | |||
|             <p class="right" id="version"></p> | |||
|         </div> | |||
|         <script type="text/javascript"> | |||
|             function parseSys(obj) { | |||
|                 document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; | |||
|             } | |||
| 
 | |||
|             getAjax("/api/system", parseSys); | |||
|         </script> | |||
|     </body> | |||
| </html> | |||
| @ -1,43 +1,118 @@ | |||
| <!doctype html> | |||
| <html> | |||
|     <head> | |||
|         <title>Index - {DEVICE}</title> | |||
|         <title>Live</title> | |||
|         <link rel="stylesheet" type="text/css" href="style.css"/> | |||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
|         <meta name="apple-mobile-web-app-capable" content="yes"> | |||
|         <script type="text/javascript"> | |||
|             getAjax('/livedata', 'livedata'); | |||
|             window.setInterval("getAjax('/livedata', 'livedata')", {JS_TS}); | |||
| 
 | |||
|             function getAjax(url, resid) { | |||
|                 var http = null; | |||
|                 http = new XMLHttpRequest(); | |||
|                 if(http != null) { | |||
|                    http.open("GET", url, true); | |||
|                    http.onreadystatechange = print; | |||
|                    http.send(null); | |||
|                 } | |||
| 
 | |||
|                 function print() { | |||
|                     if(http.readyState == 4) { | |||
|                         document.getElementById(resid).innerHTML = http.responseText; | |||
|                     } | |||
|                 } | |||
|             } | |||
|         </script> | |||
|         <style type="text/css"> | |||
|         </style> | |||
|         <script type="text/javascript" src="api.js"></script> | |||
|     </head> | |||
|     <body> | |||
|         <h1>AHOY - {DEVICE}</h1> | |||
|         <h1>AHOY</h1> | |||
|         <div id="content" class="content"> | |||
|             <div id="livedata"></div> | |||
|             <p>Every {TS}seconds the values are updated</p> | |||
|             <div id="live"></div> | |||
|             <p>Every <span id="refresh"></span> seconds the values are updated</p> | |||
|         </div> | |||
|         <div id="footer"> | |||
|             <p class="left">© 2022</p> | |||
|             <p class="left"><a href="/">Home</a></p> | |||
|             <p class="right">AHOY :: {VERSION}</p> | |||
|             <p class="right" id="version"></p> | |||
|         </div> | |||
|         <script type="text/javascript"> | |||
|             var intervalSet = false; | |||
|             function parseSys(obj) { | |||
|                 document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; | |||
|             } | |||
| 
 | |||
|             function parseIv(obj, root) { | |||
|                 var ivHtml = []; | |||
| 
 | |||
|                 var tDiv = div(["ch-all", "iv"]); | |||
|                 tDiv.appendChild(span("Total", ["head"])); | |||
|                 var total = new Array(root.ch0_fld_names.length).fill(0); | |||
|                 if(obj.length > 1) | |||
|                     ivHtml.push(tDiv); | |||
| 
 | |||
|                 for(var iv of obj) { | |||
|                     main = div(["iv"]); | |||
|                     var ch0 = div(["ch-iv"]); | |||
|                     var limit = iv["power_limit_read"] + "%"; | |||
|                     if(limit == "65535%") | |||
|                         limit = "n/a"; | |||
|                     ch0.appendChild(span(iv["name"] + " Limit " + limit + " | last Alarm: " + iv["last_alarm"], ["head"])); | |||
| 
 | |||
|                     for(var j = 0; j < root.ch0_fld_names.length; j++) { | |||
|                         var val = Math.round(iv["ch"][0][j] * 100) / 100; | |||
|                         if(val > 0) { | |||
|                             var sub = div(["subgrp"]); | |||
|                             sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"])); | |||
|                             sub.appendChild(span(root["ch0_fld_names"][j], ["info"])); | |||
|                             ch0.appendChild(sub); | |||
| 
 | |||
|                             switch(j) { | |||
|                                 case 2:  total[j] += val; break; // P_AC | |||
|                                 case 6:  total[j] += val; break; // YieldTotal | |||
|                                 case 7:  total[j] += val; break; // YieldDay | |||
|                                 case 8:  total[j] += val; break; // P_DC | |||
|                                 case 10: total[j] += val; break; // Q_AC | |||
|                             } | |||
|                         } | |||
|                     } | |||
|                     main.appendChild(ch0); | |||
| 
 | |||
| 
 | |||
|                     for(var i = 1; i < (iv["channels"] + 1); i++) { | |||
|                         var ch = div(["ch"]); | |||
|                         ch.appendChild(span(("" == iv["ch_names"][i]) ? ("CHANNEL " + i) : iv["ch_names"][i], ["head"])); | |||
| 
 | |||
|                         for(var j = 0; j < root.fld_names.length; j++) { | |||
|                             var val = Math.round(iv["ch"][i][j] * 100) / 100; | |||
|                             if(val > 0) { | |||
|                                 ch.appendChild(span(val + " " + span(root["fld_units"][j], ["unit"]).innerHTML, ["value"])); | |||
|                                 ch.appendChild(span(root["fld_names"][j], ["info"])); | |||
|                             } | |||
|                         } | |||
|                         main.appendChild(ch); | |||
|                     } | |||
| 
 | |||
|                     var ts = div(["ts"]); | |||
|                     var date = new Date(iv["ts_last_success"] * 1000); | |||
|                     ts.innerHTML = "Last received data requested at: " + date.toLocaleString('de-DE', {timeZone: 'UTC'}); | |||
|                     main.appendChild(ts); | |||
|                     ivHtml.push(main); | |||
|                 } | |||
| 
 | |||
|                 // total | |||
|                 if(obj.length > 1) { | |||
|                     for(var j = 0; j < root.ch0_fld_names.length; j++) { | |||
|                         var val = total[j]; | |||
|                         if(val > 0) { | |||
|                             var sub = div(["subgrp"]); | |||
|                             sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"])); | |||
|                             sub.appendChild(span(root["ch0_fld_names"][j], ["info"])); | |||
|                             tDiv.appendChild(sub); | |||
|                         } | |||
|                     } | |||
|                 } | |||
| 
 | |||
|                 document.getElementById("live").replaceChildren(...ivHtml); | |||
|             } | |||
| 
 | |||
|             function parse(obj) { | |||
|                 if(null != obj) { | |||
|                     parseSys(obj["system"]); | |||
|                     parseIv(obj["inverter"], obj); | |||
|                     document.getElementById("refresh").innerHTML = obj["refresh_interval"]; | |||
|                     if(false == intervalSet) { | |||
|                         window.setInterval("getAjax('/api/live', parse)", obj["refresh_interval"] * 1000); | |||
|                         intervalSet = true; | |||
|                     } | |||
|                 } | |||
|                 else | |||
|                     document.getElementById("refresh").innerHTML = "n/a"; | |||
|             } | |||
| 
 | |||
|             getAjax("/api/live", parse); | |||
|         </script> | |||
|     </body> | |||
| </html> | |||
|  | |||
| @ -0,0 +1,415 @@ | |||
| //-----------------------------------------------------------------------------
 | |||
| // 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
 | |||
| // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
 | |||
| //-----------------------------------------------------------------------------
 | |||
| 
 | |||
| #if defined(ESP32) && defined(F) | |||
|   #undef F | |||
|   #define F(sl) (sl) | |||
| #endif | |||
| 
 | |||
| #include "webApi.h" | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| webApi::webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]) { | |||
|     mSrv = srv; | |||
|     mApp = app; | |||
|     mSysCfg  = sysCfg; | |||
|     mConfig  = config; | |||
|     mStat    = stat; | |||
|     mVersion = version; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::setup(void) { | |||
|     mSrv->on("/api", HTTP_GET,  std::bind(&webApi::onApi,         this, std::placeholders::_1)); | |||
|     mSrv->on("/api", HTTP_POST, std::bind(&webApi::onApiPost,     this, std::placeholders::_1)).onBody( | |||
|                                 std::bind(&webApi::onApiPostBody, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::loop(void) { | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::onApi(AsyncWebServerRequest *request) { | |||
|     AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192); | |||
|     JsonObject root = response->getRoot(); | |||
| 
 | |||
|     Inverter<> *iv = mApp->mSys->getInverterByPos(0, false); | |||
|     String path = request->url().substring(5); | |||
|     if(path == "system")             getSystem(root); | |||
|     else if(path == "statistics")    getStatistics(root); | |||
|     else if(path == "inverter/list") getInverterList(root); | |||
|     else if(path == "index")         getIndex(root); | |||
|     else if(path == "setup")         getSetup(root); | |||
|     else if(path == "live")          getLive(root); | |||
|     else if(path == "record/info")   getRecord(root, iv->getRecordStruct(InverterDevInform_All)); | |||
|     else if(path == "record/alarm")  getRecord(root, iv->getRecordStruct(AlarmData)); | |||
|     else if(path == "record/config") getRecord(root, iv->getRecordStruct(SystemConfigPara)); | |||
|     else if(path == "record/live")   getRecord(root, iv->getRecordStruct(RealTimeRunData_Debug)); | |||
|     else | |||
|         getNotFound(root, F("http://") + request->host() + F("/api/")); | |||
| 
 | |||
|     response->addHeader("Access-Control-Allow-Origin", "*"); | |||
|     response->addHeader("Access-Control-Allow-Headers", "content-type"); | |||
|     response->setLength(); | |||
|     request->send(response); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::onApiPost(AsyncWebServerRequest *request) { | |||
|     DPRINTLN(DBG_VERBOSE, "onApiPost"); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::onApiPostBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { | |||
|     DPRINTLN(DBG_VERBOSE, "onApiPostBody"); | |||
|     DynamicJsonDocument json(200); | |||
|     AsyncJsonResponse* response = new AsyncJsonResponse(false, 200); | |||
|     JsonObject root = response->getRoot(); | |||
| 
 | |||
|     DeserializationError err = deserializeJson(json, (const char *)data); | |||
|     root[F("success")] = (err) ? false : true; | |||
|     if(!err) { | |||
|         String path = request->url().substring(5); | |||
|         if(path == "ctrl") | |||
|             root[F("success")] = setCtrl(json, root); | |||
|         else if(path == "setup") | |||
|             root[F("success")] = setSetup(json, root); | |||
|         else { | |||
|             root[F("success")] = false; | |||
|             root[F("error")]   = "Path not found: " + path; | |||
|         } | |||
|     } | |||
|     else { | |||
|         switch (err.code()) { | |||
|             case DeserializationError::Ok: break; | |||
|             case DeserializationError::InvalidInput: root[F("error")] = F("Invalid input");          break; | |||
|             case DeserializationError::NoMemory:     root[F("error")] = F("Not enough memory");      break; | |||
|             default:                                 root[F("error")] = F("Deserialization failed"); break; | |||
|         } | |||
|     } | |||
| 
 | |||
|     response->setLength(); | |||
|     request->send(response); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getNotFound(JsonObject obj, String url) { | |||
|     JsonObject ep = obj.createNestedObject("avail_endpoints"); | |||
|     ep[F("system")]        = url + F("system"); | |||
|     ep[F("statistics")]    = url + F("statistics"); | |||
|     ep[F("inverter/list")] = url + F("inverter/list"); | |||
|     ep[F("index")]         = url + F("index"); | |||
|     ep[F("setup")]         = url + F("setup"); | |||
|     ep[F("live")]          = url + F("live"); | |||
|     ep[F("record/info")]   = url + F("record/info"); | |||
|     ep[F("record/alarm")]  = url + F("record/alarm"); | |||
|     ep[F("record/config")] = url + F("record/config"); | |||
|     ep[F("record/live")]   = url + F("record/live"); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getSystem(JsonObject obj) { | |||
|     obj[F("ssid")]        = mSysCfg->stationSsid; | |||
|     obj[F("device_name")] = mSysCfg->deviceName; | |||
|     obj[F("version")]     = String(mVersion); | |||
|     obj[F("build")]       = String(AUTO_GIT_HASH); | |||
|     obj[F("ts_uptime")]   = mApp->getUptime(); | |||
|     obj[F("ts_now")]      = mApp->getTimestamp(); | |||
|     obj[F("wifi_rssi")]   = WiFi.RSSI(); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getStatistics(JsonObject obj) { | |||
|     obj[F("rx_success")]     = mStat->rxSuccess; | |||
|     obj[F("rx_fail")]        = mStat->rxFail; | |||
|     obj[F("rx_fail_answer")] = mStat->rxFailNoAnser; | |||
|     obj[F("frame_cnt")]      = mStat->frmCnt; | |||
|     obj[F("tx_cnt")]         = mApp->mSys->Radio.mSendCnt; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getInverterList(JsonObject obj) { | |||
|     JsonArray invArr = obj.createNestedArray(F("inverter")); | |||
| 
 | |||
|     Inverter<> *iv; | |||
|     for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { | |||
|         iv = mApp->mSys->getInverterByPos(i); | |||
|         if(NULL != iv) { | |||
|             JsonObject obj2 = invArr.createNestedObject(); | |||
|             obj2[F("id")]       = i; | |||
|             obj2[F("name")]     = String(iv->name); | |||
|             obj2[F("serial")]   = String(iv->serial.u64, HEX); | |||
|             obj2[F("channels")] = iv->channels; | |||
|             obj2[F("version")]  = String(iv->fwVersion); | |||
| 
 | |||
|             for(uint8_t j = 0; j < iv->channels; j ++) { | |||
|                 obj2[F("ch_max_power")][j] = iv->chMaxPwr[j]; | |||
|                 obj2[F("ch_name")][j] = iv->chName[j]; | |||
|             } | |||
|         } | |||
|     } | |||
|     obj[F("interval")]          = String(mConfig->sendInterval); | |||
|     obj[F("retries")]           = String(mConfig->maxRetransPerPyld); | |||
|     obj[F("max_num_inverters")] = MAX_NUM_INVERTERS; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getMqtt(JsonObject obj) { | |||
|     obj[F("broker")] = String(mConfig->mqtt.broker); | |||
|     obj[F("port")]   = String(mConfig->mqtt.port); | |||
|     obj[F("user")]   = String(mConfig->mqtt.user); | |||
|     obj[F("pwd")]    = (strlen(mConfig->mqtt.pwd) > 0) ? F("{PWD}") : String(""); | |||
|     obj[F("topic")]  = String(mConfig->mqtt.topic); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getNtp(JsonObject obj) { | |||
|     obj[F("addr")] = String(mConfig->ntpAddr); | |||
|     obj[F("port")] = String(mConfig->ntpPort); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getPinout(JsonObject obj) { | |||
|     obj[F("cs")]  = mConfig->pinCs; | |||
|     obj[F("ce")]  = mConfig->pinCe; | |||
|     obj[F("irq")] = mConfig->pinIrq; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getRadio(JsonObject obj) { | |||
|     obj[F("power_level")] = mConfig->amplifierPower; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getSerial(JsonObject obj) { | |||
|     obj[F("interval")]       = (uint16_t)mConfig->serialInterval; | |||
|     obj[F("show_live_data")] = mConfig->serialShowIv; | |||
|     obj[F("debug")]          = mConfig->serialDebug; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getIndex(JsonObject obj) { | |||
|     getSystem(obj.createNestedObject(F("system"))); | |||
|     getStatistics(obj.createNestedObject(F("statistics"))); | |||
|     obj["refresh_interval"] = SEND_INTERVAL; | |||
| 
 | |||
|     JsonArray inv = obj.createNestedArray(F("inverter")); | |||
|     Inverter<> *iv; | |||
|     for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { | |||
|         iv = mApp->mSys->getInverterByPos(i); | |||
|         if(NULL != iv) { | |||
|             record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); | |||
|             JsonObject invObj = inv.createNestedObject(); | |||
|             invObj[F("id")]              = i; | |||
|             invObj[F("name")]            = String(iv->name); | |||
|             invObj[F("version")]         = String(iv->fwVersion); | |||
|             invObj[F("is_avail")]        = iv->isAvailable(mApp->getTimestamp(), rec); | |||
|             invObj[F("is_producing")]    = iv->isProducing(mApp->getTimestamp(), rec); | |||
|             invObj[F("ts_last_success")] = iv->getLastTs(rec); | |||
|         } | |||
|     } | |||
| 
 | |||
|     JsonArray warn = obj.createNestedArray(F("warnings")); | |||
|     if(!mApp->mSys->Radio.isChipConnected()) | |||
|         warn.add(F("your NRF24 module can't be reached, check the wiring and pinout")); | |||
|     if(!mApp->mqttIsConnected()) | |||
|         warn.add(F("MQTT is not connected")); | |||
| 
 | |||
|     JsonArray info = obj.createNestedArray(F("infos")); | |||
|     if(mApp->getRebootRequestState()) | |||
|         info.add(F("reboot your ESP to apply all your configuration changes!")); | |||
|     if(!mApp->getSettingsValid()) | |||
|         info.add(F("your settings are invalid")); | |||
|     if(mApp->mqttIsConnected()) | |||
|         info.add(F("MQTT is connected")); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getSetup(JsonObject obj) { | |||
|     getSystem(obj.createNestedObject(F("system"))); | |||
|     getInverterList(obj.createNestedObject(F("inverter"))); | |||
|     getMqtt(obj.createNestedObject(F("mqtt"))); | |||
|     getNtp(obj.createNestedObject(F("ntp"))); | |||
|     getPinout(obj.createNestedObject(F("pinout"))); | |||
|     getRadio(obj.createNestedObject(F("radio"))); | |||
|     getSerial(obj.createNestedObject(F("serial"))); | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getLive(JsonObject obj) { | |||
|     getSystem(obj.createNestedObject(F("system"))); | |||
|     JsonArray invArr = obj.createNestedArray(F("inverter")); | |||
|     obj["refresh_interval"] = SEND_INTERVAL; | |||
| 
 | |||
|     uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_Q}; | |||
| 
 | |||
|     Inverter<> *iv; | |||
|     uint8_t pos; | |||
|     for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { | |||
|         iv = mApp->mSys->getInverterByPos(i); | |||
|         if(NULL != iv) { | |||
|             record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); | |||
|             JsonObject obj2 = invArr.createNestedObject(); | |||
|             obj2[F("name")]               = String(iv->name); | |||
|             obj2[F("channels")]           = iv->channels; | |||
|             obj2[F("power_limit_read")]   = round3(iv->actPowerLimit); | |||
|             obj2[F("last_alarm")]         = String(iv->lastAlarmMsg); | |||
|             obj2[F("ts_last_success")]    = rec->ts; | |||
| 
 | |||
|             JsonArray ch = obj2.createNestedArray("ch"); | |||
|             JsonArray ch0 = ch.createNestedArray(); | |||
|             obj2[F("ch_names")][0] = "AC"; | |||
|             for (uint8_t fld = 0; fld < sizeof(list); fld++) { | |||
|                 pos = (iv->getPosByChFld(CH0, list[fld], rec)); | |||
|                 ch0[fld] = (0xff != pos) ? round3(iv->getValue(pos, rec)) : 0.0; | |||
|                 obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; | |||
|                 obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; | |||
|             } | |||
| 
 | |||
|             for(uint8_t j = 1; j <= iv->channels; j ++) { | |||
|                 obj2[F("ch_names")][j] = String(iv->chName[j-1]); | |||
|                 JsonArray cur = ch.createNestedArray(); | |||
|                 for (uint8_t k = 0; k < 6; k++) { | |||
|                     switch(k) { | |||
|                         default: pos = (iv->getPosByChFld(j, FLD_UDC, rec)); break; | |||
|                         case 1:  pos = (iv->getPosByChFld(j, FLD_IDC, rec)); break; | |||
|                         case 2:  pos = (iv->getPosByChFld(j, FLD_PDC, rec)); break; | |||
|                         case 3:  pos = (iv->getPosByChFld(j, FLD_YD, rec));  break; | |||
|                         case 4:  pos = (iv->getPosByChFld(j, FLD_YT, rec));  break; | |||
|                         case 5:  pos = (iv->getPosByChFld(j, FLD_IRR, rec)); break; | |||
|                     } | |||
|                     cur[k] = (0xff != pos) ? round3(iv->getValue(pos, rec)) : 0.0; | |||
|                     if(1 == j) { | |||
|                         obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; | |||
|                         obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; | |||
|                     } | |||
|                 } | |||
|             } | |||
|         } | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| void webApi::getRecord(JsonObject obj, record_t<> *rec) { | |||
|     JsonArray invArr = obj.createNestedArray(F("inverter")); | |||
| 
 | |||
|     Inverter<> *iv; | |||
|     uint8_t pos; | |||
|     for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { | |||
|         iv = mApp->mSys->getInverterByPos(i); | |||
|         if(NULL != iv) { | |||
|             JsonArray obj2 = invArr.createNestedArray(); | |||
|             for(uint8_t j = 0; j < rec->length; j++) { | |||
|                 byteAssign_t *assign = iv->getByteAssign(j, rec); | |||
|                 pos = (iv->getPosByChFld(assign->ch, assign->fieldId, rec)); | |||
|                 obj2[j]["fld"]  = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; | |||
|                 obj2[j]["unit"] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; | |||
|                 obj2[j]["val"]  = (0xff != pos) ? String(iv->getValue(pos, rec)) : notAvail; | |||
|             } | |||
|         } | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| bool webApi::setCtrl(DynamicJsonDocument jsonIn, JsonObject jsonOut) { | |||
|     uint8_t cmd = jsonIn[F("cmd")]; | |||
| 
 | |||
|     // Todo: num is the inverter number 0-3. For better display in DPRINTLN
 | |||
|     uint8_t num = jsonIn[F("inverter")];  | |||
|     uint8_t tx_request = jsonIn[F("tx_request")];  | |||
| 
 | |||
|     if(TX_REQ_DEVCONTROL == tx_request)  | |||
|     { | |||
|         DPRINTLN(DBG_INFO, F("devcontrol [") + String(num) + F("], cmd: 0x") + String(cmd, HEX)); | |||
|          | |||
|         Inverter<> *iv = getInverter(jsonIn, jsonOut); | |||
|         JsonArray payload = jsonIn[F("payload")].as<JsonArray>(); | |||
| 
 | |||
|         if(NULL != iv)  | |||
|         { | |||
|             switch (cmd) | |||
|             { | |||
|                 case TurnOn: | |||
|                     iv->devControlCmd = TurnOn; | |||
|                     iv->devControlRequest = true; | |||
|                     break; | |||
|                 case TurnOff: | |||
|                     iv->devControlCmd = TurnOff; | |||
|                     iv->devControlRequest = true; | |||
|                     break; | |||
|                 case CleanState_LockAndAlarm: | |||
|                     iv->devControlCmd = CleanState_LockAndAlarm; | |||
|                     iv->devControlRequest = true; | |||
|                     break; | |||
|                 case Restart: | |||
|                     iv->devControlCmd = Restart; | |||
|                     iv->devControlRequest = true; | |||
|                     break; | |||
|                 case ActivePowerContr: | |||
|                     iv->devControlCmd = ActivePowerContr; | |||
|                     iv->devControlRequest = true; | |||
|                     iv->powerLimit[0] = payload[0]; | |||
|                     iv->powerLimit[1] = payload[1]; | |||
|                     break; | |||
|                 default: | |||
|                     jsonOut["error"] = "unknown 'cmd' = " + String(cmd); | |||
|                     return false; | |||
|             }             | |||
|         } else { | |||
|             return false; | |||
|         } | |||
|     } | |||
|     else { | |||
|         jsonOut[F("error")] = F("unknown 'tx_request'"); | |||
|         return false; | |||
|     } | |||
| 
 | |||
|     return true; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| bool webApi::setSetup(DynamicJsonDocument jsonIn, JsonObject jsonOut) { | |||
|     if(F("set_time") == jsonIn[F("cmd")]) | |||
|         mApp->setTimestamp(jsonIn[F("ts")]); | |||
|     else if(F("sync_ntp") == jsonIn[F("cmd")]) | |||
|         mApp->setTimestamp(0); // 0: update ntp flag
 | |||
|     else { | |||
|         jsonOut[F("error")] = F("unknown cmd"); | |||
|         return false; | |||
|     } | |||
| 
 | |||
|     return true; | |||
| } | |||
| 
 | |||
| 
 | |||
| //-----------------------------------------------------------------------------
 | |||
| Inverter<> *webApi::getInverter(DynamicJsonDocument jsonIn, JsonObject jsonOut) { | |||
|     uint8_t id = jsonIn[F("inverter")]; | |||
|     Inverter<> *iv = mApp->mSys->getInverterByPos(id); | |||
|     if(NULL == iv) | |||
|         jsonOut[F("error")] = F("inverter index to high: ") + String(id); | |||
|     return iv; | |||
| } | |||
| @ -0,0 +1,62 @@ | |||
| #ifndef __WEB_API_H__ | |||
| #define __WEB_API_H__ | |||
| 
 | |||
| #include "dbg.h" | |||
| #ifdef ESP32 | |||
|     #include "AsyncTCP.h" | |||
| #else | |||
|     #include "ESPAsyncTCP.h" | |||
| #endif | |||
| #include "ESPAsyncWebServer.h" | |||
| #include "AsyncJson.h" | |||
| #include "app.h" | |||
| 
 | |||
| 
 | |||
| class app; | |||
| 
 | |||
| class webApi { | |||
|     public:  | |||
|         webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]); | |||
| 
 | |||
|         void setup(void); | |||
|         void loop(void); | |||
| 
 | |||
|     private: | |||
|         void onApi(AsyncWebServerRequest *request); | |||
|         void onApiPost(AsyncWebServerRequest *request); | |||
|         void onApiPostBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); | |||
|         void getNotFound(JsonObject obj, String url); | |||
| 
 | |||
|         void getSystem(JsonObject obj); | |||
|         void getStatistics(JsonObject obj); | |||
|         void getInverterList(JsonObject obj); | |||
|         void getMqtt(JsonObject obj); | |||
|         void getNtp(JsonObject obj); | |||
|         void getPinout(JsonObject obj); | |||
|         void getRadio(JsonObject obj); | |||
|         void getSerial(JsonObject obj); | |||
| 
 | |||
|         void getIndex(JsonObject obj); | |||
|         void getSetup(JsonObject obj); | |||
|         void getLive(JsonObject obj); | |||
|         void getRecord(JsonObject obj, record_t<> *rec); | |||
| 
 | |||
|         bool setCtrl(DynamicJsonDocument jsonIn, JsonObject jsonOut); | |||
|         bool setSetup(DynamicJsonDocument jsonIn, JsonObject jsonOut); | |||
| 
 | |||
|         Inverter<> *getInverter(DynamicJsonDocument jsonIn, JsonObject jsonOut); | |||
| 
 | |||
|         double round3(double value) { | |||
|            return (int)(value * 1000 + 0.5) / 1000.0; | |||
|         } | |||
| 
 | |||
|         AsyncWebServer *mSrv; | |||
|         app *mApp; | |||
| 
 | |||
|         config_t *mConfig; | |||
|         sysConfig_t *mSysCfg; | |||
|         statistics_t *mStat; | |||
|         char *mVersion; | |||
| }; | |||
| 
 | |||
| #endif /*__WEB_API_H__*/ | |||
					Loading…
					
					
				
		Reference in new issue