21
21
# or any other firmware please follow instructions here:
22
22
# https://github.com/mongoose-os-apps/shelly-homekit/wiki
23
23
#
24
- # usage: flash-shelly.py [-h] [-m {homekit,keep,revert}] [-a] [-q] [-l] [-e [EXCLUDE ...]] [-n] [-y] [-V VERSION] [-c HAP_SETUP_CODE] [--variant VARIANT] [-v {0,1}] [hosts ...]
24
+ # usage: flash-shelly.py [-h] [-m {homekit,keep,revert}] [-a] [-q] [-l] [-e [EXCLUDE ...]] [-n] [-y] [-V VERSION] [-c HAP_SETUP_CODE] [-v {0,1,2,3,4,5}] [--variant VARIANT] [--log-file LOG_FILENAME]
25
+ # [hosts ...]
25
26
#
26
27
# Shelly HomeKit flashing script utility
27
28
#
43
44
# Force a particular version.
44
45
# -c HAP_SETUP_CODE, --hap-setup-code HAP_SETUP_CODE
45
46
# Configure HomeKit setup code, after flashing.
47
+ # -v {0,1,2,3,4,5}, --verbose {0,1,2,3,4,5}
48
+ # Enable verbose logging 0=critical, 1=error, 2=warning, 3=info, 4=debug, 5=trace.
46
49
# --variant VARIANT Prerelease variant name.
47
- # -v {0,1}, --verbose {0,1}
48
- # Enable verbose logging level .
50
+ # --log-file LOG_FILENAME
51
+ # Create output log file with chosen filename .
49
52
50
53
51
54
import argparse
64
67
logging .addLevelName (logging .TRACE , 'TRACE' )
65
68
logging .Logger .trace = functools .partialmethod (logging .Logger .log , logging .TRACE )
66
69
logging .trace = functools .partial (logging .log , logging .TRACE )
67
- logging .basicConfig (format = '%(message)s' )
68
70
logger = logging .getLogger (__name__ )
69
- logger .setLevel (logging .INFO )
71
+ logger .setLevel (logging .TRACE )
72
+ log_level = {'0' : logging .CRITICAL ,
73
+ '1' : logging .ERROR ,
74
+ '2' : logging .WARNING ,
75
+ '3' : logging .INFO ,
76
+ '4' : logging .DEBUG ,
77
+ '5' : logging .TRACE }
70
78
71
79
upgradeable_devices = 0
80
+ flashed_devices = 0
72
81
arch = platform .system ()
73
- # Windows does not support acsii colours
74
- if not arch .startswith ('Win' ):
75
- WHITE = '\033 [1m'
76
- RED = '\033 [1;91m'
77
- GREEN = '\033 [1;92m'
78
- YELLOW = '\033 [1;93m'
79
- BLUE = '\033 [1;94m'
80
- PURPLE = '\033 [1;95m'
81
- NC = '\033 [0m'
82
- else :
83
- WHITE = ''
84
- RED = ''
85
- GREEN = ''
86
- YELLOW = ''
87
- BLUE = ''
88
- PURPLE = ''
89
- NC = ''
90
82
91
83
def upgrade_pip ():
92
84
logger .info ("Updating pip..." )
@@ -112,6 +104,7 @@ def __init__(self):
112
104
self .queue = queue .Queue ()
113
105
114
106
def add_service (self , zeroconf , type , device ):
107
+ logger .trace (f"[Device Scan] found device: { device } " )
115
108
device = device .replace ('._http._tcp.local.' , '' )
116
109
self .queue .put (Device (device ))
117
110
@@ -139,16 +132,16 @@ def is_valid_hostname(self, hostname):
139
132
result = False
140
133
allowed = re .compile ("(?!-)[A-Z\d-]{1,63}(?<!-)$" , re .IGNORECASE )
141
134
result = all (allowed .match (x ) for x in hostname .split ("." ))
142
- logger .trace (f"Valid Hostname: { hostname } { result } " )
135
+ logger .debug (f"Valid Hostname: { hostname } { result } " )
143
136
return result
144
137
145
138
def is_host_online (self , host ):
146
139
try :
147
140
self .wifi_ip = socket .gethostbyname (host )
148
- logger .trace (f"Hostname: { host } is Online" )
141
+ logger .debug (f"Hostname: { host } is Online" )
149
142
return True
150
143
except :
151
- logger .warning (f"\n { RED } Could not resolve host: { host } { NC } " )
144
+ logger .error (f"\n { RED } Could not resolve host: { host } { NC } " )
152
145
return False
153
146
154
147
def get_device_url (self ):
@@ -178,7 +171,7 @@ def get_device_info(self):
178
171
except requests .exceptions .RequestException as err :
179
172
logger .debug (f"Error: { err } " )
180
173
else :
181
- logger .debug (f"{ RED } Could not get info from device: { self .host } \n { NC } " )
174
+ logger .debug (f"Could not get info from device: { self .host } " )
182
175
self .info = info
183
176
return info
184
177
@@ -226,14 +219,12 @@ def shelly_model(self, type):
226
219
'SHGS-1' : 'ShellyGas' ,
227
220
'SHEM' : 'ShellyEM' ,
228
221
'SHEM-3' : 'Shelly3EM' ,
222
+ 'SHSEN-1' : 'ShellySensor1' ,
229
223
'switch1' : 'Shelly1' ,
230
224
'switch1pm' : 'Shelly1PM' ,
231
225
'switch2' : 'Shelly2' ,
232
226
'switch25' : 'Shelly25' ,
233
227
'shelly-plug-s' : 'ShellyPlugS' ,
234
- 'dimmer1' : 'ShellyDimmer1' ,
235
- 'dimmer2' : 'ShellyDimmer2' ,
236
- 'rgbw2' : 'ShellyRGBW2' ,
237
228
}
238
229
return options .get (type , type )
239
230
@@ -341,7 +332,7 @@ def flash_firmware(self):
341
332
dlurl = self .dlurl .replace ('https' , 'http' )
342
333
logger .debug (f"curl -qsS http://{ self .wifi_ip } /ota?url={ dlurl } " )
343
334
response = requests .get (f'http://{ self .wifi_ip } /ota?url={ dlurl } ' )
344
- logger .debug (response .text )
335
+ logger .trace (response .text )
345
336
346
337
347
338
def parse_version (vs ):
@@ -385,12 +376,12 @@ def write_hap_setup_code(wifi_ip, hap_setup_code):
385
376
value = {'code' : hap_setup_code }
386
377
logger .debug (f"requests.post(url='http://{ wifi_ip } /rpc/HAP.Setup', json={ value } " )
387
378
response = requests .post (url = f'http://{ wifi_ip } /rpc/HAP.Setup' , json = {'code' : hap_setup_code })
388
- logger .debug (response .text )
379
+ logger .trace (response .text )
389
380
if response .text .startswith ('null' ):
390
381
logger .info (f"Done." )
391
382
392
383
def write_flash (device_info , hap_setup_code ):
393
- logger .debug (f"\n { WHITE } write_flash { NC } " )
384
+ logger .debug (f"{ PURPLE } [Write Flash] { NC } " )
394
385
flashed = False
395
386
device_info .flash_firmware ()
396
387
logger .info (f"waiting for { device_info .friendly_host } to reboot..." )
@@ -408,7 +399,9 @@ def write_flash(device_info, hap_setup_code):
408
399
break
409
400
time .sleep (2 )
410
401
if onlinecheck == device_info .flash_fw_version :
411
- logger .info (f"{ GREEN } Successfully flashed { device_info .friendly_host } to { device_info .flash_fw_version } { NC } " )
402
+ global flashed_devices
403
+ flashed_devices += 1
404
+ logger .critical (f"{ GREEN } Successfully flashed { device_info .friendly_host } to { device_info .flash_fw_version } { NC } " )
412
405
if hap_setup_code :
413
406
write_hap_setup_code (device_info .wifi_ip , hap_setup_code )
414
407
else :
@@ -428,8 +421,8 @@ def write_flash(device_info, hap_setup_code):
428
421
logger .debug ("Current: %s" % onlinecheck )
429
422
430
423
def parse_info (device_info , action , dry_run , quiet_run , silent_run , mode , exclude , hap_setup_code , requires_upgrade ):
431
- logger .info (f"" )
432
- logger .debug (f"{ WHITE } parse_info { NC } " )
424
+ logger .debug (f"" )
425
+ logger .debug (f"{ PURPLE } [Parse Info] { NC } " )
433
426
logger .trace (f"device_info: { device_info } " )
434
427
435
428
perform_flash = False
@@ -455,14 +448,16 @@ def parse_info(device_info, action, dry_run, quiet_run, silent_run, mode, exclud
455
448
logger .debug (f"host: { host } " )
456
449
logger .debug (f"device_name: { device_name } " )
457
450
logger .debug (f"device_id: { device_id } " )
451
+ logger .debug (f"wifi_ip: { wifi_ip } " )
458
452
logger .debug (f"model: { model } " )
459
453
logger .debug (f"stock_model: { stock_model } " )
460
454
logger .debug (f"colour_mode: { colour_mode } " )
455
+ logger .debug (f"sys_temp: { sys_temp } " )
461
456
logger .debug (f"action: { action } " )
462
457
logger .debug (f"flash mode: { mode } " )
463
458
logger .debug (f"requires_upgrade: { requires_upgrade } " )
464
- logger .debug (f"current_fw_version: { current_fw_version } " )
465
- logger .debug (f"flash_fw_version: { flash_fw_version } " )
459
+ logger .debug (f"current_fw_version: { current_fw_type_str } { current_fw_version } " )
460
+ logger .debug (f"flash_fw_version: { flash_fw_type_str } { flash_fw_version } " )
466
461
logger .debug (f"force_version: { force_version } " )
467
462
logger .debug (f"dlurl: { dlurl } " )
468
463
@@ -486,6 +481,7 @@ def parse_info(device_info, action, dry_run, quiet_run, silent_run, mode, exclud
486
481
latest_fw_label = flash_fw_version
487
482
488
483
if (not quiet_run or (quiet_run and (is_newer (flash_fw_version , current_fw_version ) or force_version and dlurl and parse_version (flash_fw_version ) != parse_version (current_fw_version )))) and requires_upgrade != 'Done' :
484
+ logger .info (f"" )
489
485
logger .info (f"{ WHITE } Host: { NC } http://{ host } " )
490
486
logger .info (f"{ WHITE } Device Name: { NC } { device_name } " )
491
487
logger .info (f"{ WHITE } Device ID: { NC } { device_id } " )
@@ -543,7 +539,7 @@ def parse_info(device_info, action, dry_run, quiet_run, silent_run, mode, exclud
543
539
logger .info ("Does not need flashing..." )
544
540
return 0
545
541
546
- logger .debug (f"\n perform_flash : { perform_flash } \n " )
542
+ logger .debug (f"perform_flash : { perform_flash } " )
547
543
if perform_flash == True and dry_run == False and silent_run == False :
548
544
if requires_upgrade == True :
549
545
flash_message = f"{ message } { keyword } "
@@ -564,9 +560,10 @@ def parse_info(device_info, action, dry_run, quiet_run, silent_run, mode, exclud
564
560
write_flash (device_info , hap_setup_code )
565
561
566
562
def probe_device (device , action , dry_run , quiet_run , silent_run , mode , exclude , version , variant , hap_setup_code ):
567
- logger .debug (f"\n { WHITE } probe_device{ NC } " )
563
+ logger .debug ("" )
564
+ logger .debug (f"{ PURPLE } [Probe Device]{ NC } " )
568
565
d_info = json .dumps (device , indent = 4 )
569
- logger .trace (f"Device Info : { d_info } " )
566
+ logger .trace (f"device_info : { d_info } " )
570
567
571
568
requires_upgrade = False
572
569
if mode == 'keep' :
@@ -591,9 +588,9 @@ def probe_device(device, action, dry_run, quiet_run, silent_run, mode, exclude,
591
588
elif flashmode == 'stock' :
592
589
deviceinfo .update_to_stock (stock_release_info )
593
590
if deviceinfo .fw_type == "homekit" and float (f"{ parse_version (deviceinfo .info ['version' ])[0 ]} .{ parse_version (deviceinfo .info ['version' ])[1 ]} " ) < 2.1 :
594
- logger .info (f"{ WHITE } Host: { NC } { deviceinfo .host } " )
595
- logger .info (f"Version { deviceinfo .info ['version' ]} is to old for this script," )
596
- logger .info (f"please update via the device webUI.\n " )
591
+ logger .error (f"{ WHITE } Host: { NC } { deviceinfo .host } " )
592
+ logger .error (f"Version { deviceinfo .info ['version' ]} is to old for this script," )
593
+ logger .error (f"please update via the device webUI.\n " )
597
594
else :
598
595
parse_info (deviceinfo , action , dry_run , quiet_run , silent_run , flashmode , exclude , hap_setup_code , requires_upgrade )
599
596
if requires_upgrade :
@@ -607,13 +604,15 @@ def probe_device(device, action, dry_run, quiet_run, silent_run, mode, exclude,
607
604
def device_scan (hosts , action , dry_run , quiet_run , silent_run , mode , exclude , version , variant , hap_setup_code ):
608
605
if hosts :
609
606
for host in hosts :
610
- logger .debug (f"\n { WHITE } device_scan{ NC } " )
607
+ logger .debug (f"" )
608
+ logger .debug (f"{ PURPLE } [Device Scan] manual{ NC } " )
611
609
deviceinfo = Device (host )
612
610
deviceinfo .get_device_info ()
613
611
if deviceinfo .fw_type is not None :
614
612
device = {'host' : deviceinfo .host , 'wifi_ip' : deviceinfo .wifi_ip , 'fw_type' : deviceinfo .fw_type , 'device_url' : deviceinfo .device_url , 'info' : deviceinfo .info }
615
613
probe_device (device , action , dry_run , quiet_run , silent_run , mode , exclude , version , variant , hap_setup_code )
616
614
else :
615
+ logger .debug (f"{ PURPLE } [Device Scan] automatic scan{ NC } " )
617
616
logger .info (f"{ WHITE } Scanning for Shelly devices...{ NC } " )
618
617
zc = zeroconf .Zeroconf ()
619
618
listener = MyListener ()
@@ -623,10 +622,14 @@ def device_scan(hosts, action, dry_run, quiet_run, silent_run, mode, exclude, ve
623
622
try :
624
623
deviceinfo = listener .queue .get (timeout = 20 )
625
624
except queue .Empty :
626
- logger .info (f"\n { GREEN } Devices found: { total_devices } Upgradeable: { upgradeable_devices } { NC } " )
625
+ logger .info (f"" )
626
+ logger .info (f"{ GREEN } Devices found: { total_devices } Upgradeable: { upgradeable_devices } Flashed: { flashed_devices } { NC } " )
627
+ if args .log_filename :
628
+ logger .info (f"Log file created: { args .log_filename } " )
627
629
zc .close ()
628
630
break
629
- logger .debug (f"\n { WHITE } device_scan{ NC } " )
631
+ logger .debug (f"" )
632
+ logger .debug (f"{ PURPLE } [Device Scan] action queue entry{ NC } " )
630
633
deviceinfo .get_device_info ()
631
634
if deviceinfo .fw_type is not None :
632
635
device = {'host' : deviceinfo .host , 'wifi_ip' : deviceinfo .wifi_ip , 'fw_type' : deviceinfo .fw_type , 'device_url' : deviceinfo .device_url , 'info' : deviceinfo .info }
@@ -644,24 +647,50 @@ def device_scan(hosts, action, dry_run, quiet_run, silent_run, mode, exclude, ve
644
647
parser .add_argument ('-y' , '--assume-yes' , action = "store_true" , dest = 'silent_run' , default = False , help = "Do not ask any confirmation to perform the flash." )
645
648
parser .add_argument ('-V' , '--version' ,type = str , action = "store" , dest = "version" , default = False , help = "Force a particular version." )
646
649
parser .add_argument ('-c' , '--hap-setup-code' , action = "store" , dest = "hap_setup_code" , default = False , help = "Configure HomeKit setup code, after flashing." )
650
+ parser .add_argument ('-v' , '--verbose' , action = "store" , dest = "verbose" , choices = ['0' , '1' , '2' , '3' , '4' , '5' ], default = '3' , help = "Enable verbose logging 0=critical, 1=error, 2=warning, 3=info, 4=debug, 5=trace." )
647
651
parser .add_argument ('--variant' , action = "store" , dest = "variant" , default = False , help = "Prerelease variant name." )
648
- parser .add_argument ('-v' , '--verbose ' , action = "store" , dest = "verbose " , choices = [ '0' , '1' ], help = "Enable verbose logging level ." )
652
+ parser .add_argument ('--log-file ' , action = "store" , dest = "log_filename " , default = False , help = "Create output log file with chosen filename ." )
649
653
parser .add_argument ('hosts' , type = str , nargs = '*' )
650
654
args = parser .parse_args ()
651
655
action = 'list' if args .list else 'flash'
652
656
args .mode = 'stock' if args .mode == 'revert' else args .mode
653
657
args .hap_setup_code = f"{ args .hap_setup_code [:3 ]} -{ args .hap_setup_code [3 :- 3 ]} -{ args .hap_setup_code [5 :]} " if args .hap_setup_code and '-' not in args .hap_setup_code else args .hap_setup_code
654
- if args .verbose and '0' in args .verbose :
655
- logger .setLevel (logging .DEBUG )
656
- elif args .verbose and '1' in args .verbose :
657
- logger .setLevel (logging .TRACE )
658
+
659
+ sh = logging .StreamHandler ()
660
+ sh .setFormatter (logging .Formatter ('%(message)s' ))
661
+ sh .setLevel (log_level [args .verbose ])
662
+ if args .log_filename :
663
+ fh = logging .FileHandler (args .log_filename , mode = 'w' , encoding = 'utf-8' )
664
+ fh .setFormatter (logging .Formatter ('%(asctime)s %(levelname)s %(lineno)d %(message)s' ))
665
+ fh .setLevel (log_level [args .verbose ])
666
+ logger .addHandler (fh )
667
+ logger .addHandler (sh )
668
+
669
+ # Windows and log file do not support acsii colours
670
+ if not args .log_filename and not arch .startswith ('Win' ):
671
+ WHITE = '\033 [1m'
672
+ RED = '\033 [1;91m'
673
+ GREEN = '\033 [1;92m'
674
+ YELLOW = '\033 [1;93m'
675
+ BLUE = '\033 [1;94m'
676
+ PURPLE = '\033 [1;95m'
677
+ NC = '\033 [0m'
678
+ else :
679
+ WHITE = ''
680
+ RED = ''
681
+ GREEN = ''
682
+ YELLOW = ''
683
+ BLUE = ''
684
+ PURPLE = ''
685
+ NC = ''
686
+
658
687
659
688
homekit_release_info = None
660
689
stock_release_info = None
661
690
app_version = "2.1.0"
662
691
663
- logger .debug (f"{ WHITE } app_version : { app_version } { NC } " )
664
- logger .debug (f"{ PURPLE } OS : { arch } { NC } " )
692
+ logger .debug (f"OS : { PURPLE } { arch } { NC } " )
693
+ logger .debug (f"app_version : { app_version } " )
665
694
logger .debug (f"manual_hosts: { args .hosts } " )
666
695
logger .debug (f"action: { action } " )
667
696
logger .debug (f"mode: { args .mode } " )
@@ -674,6 +703,7 @@ def device_scan(hosts, action, dry_run, quiet_run, silent_run, mode, exclude, ve
674
703
logger .debug (f"exclude: { args .exclude } " )
675
704
logger .debug (f"variant: { args .variant } " )
676
705
logger .debug (f"verbose: { args .verbose } " )
706
+ logger .debug (f"log_filename: { args .log_filename } " )
677
707
678
708
if not args .hosts and not args .do_all :
679
709
logger .info (f"{ WHITE } Requires a hostname or -a | --all{ NC } " )
@@ -690,23 +720,24 @@ def device_scan(hosts, action, dry_run, quiet_run, silent_run, mode, exclude, ve
690
720
691
721
try :
692
722
fp = requests .get ("https://api.shelly.cloud/files/firmware" , timeout = 3 )
723
+ logger .debug (f"stock_release_info status code: { fp .status_code } " )
693
724
if fp .status_code == 200 :
694
725
stock_release_info = json .loads (fp .content )
695
726
except requests .exceptions .RequestException as err :
696
- logger .debug (fp .status_code )
727
+ logger .critical (f"{ RED } CRITICAL:{ NC } { err } " )
728
+ logger .trace (f"stock_release_info: { json .dumps (stock_release_info , indent = 4 )} " )
697
729
try :
698
730
fp = requests .get ("https://rojer.me/files/shelly/update.json" , timeout = 3 )
731
+ logger .debug (f"homekit_release_info status code: { fp .status_code } " )
699
732
if fp .status_code == 200 :
700
733
homekit_release_info = json .loads (fp .content )
701
734
except requests .exceptions .RequestException as err :
702
- logger .debug (err )
703
-
704
- logger .trace (f"\n { WHITE } stock_release_info:{ NC } { stock_release_info } " )
705
- logger .trace (f"\n { WHITE } homekit_release_info:{ NC } { homekit_release_info } " )
735
+ logger .critical (f"{ RED } CRITICAL:{ NC } { err } " )
736
+ logger .trace (f"homekit_release_info: { json .dumps (homekit_release_info , indent = 4 )} " )
706
737
707
738
if not stock_release_info or not homekit_release_info :
708
- logger .warning (f"{ RED } Failed to lookup online version information{ NC } " )
709
- logger .warning ("For more information please point your web browser to:" )
710
- logger .warning ("https://github.com/mongoose-os-apps/shelly-homekit/wiki/Flashing#script-fails-to-run" )
739
+ logger .error (f"{ RED } Failed to lookup online version information{ NC } " )
740
+ logger .error ("For more information please point your web browser to:" )
741
+ logger .error ("https://github.com/mongoose-os-apps/shelly-homekit/wiki/Flashing#script-fails-to-run" )
711
742
else :
712
743
device_scan (args .hosts , action , args .dry_run , args .quiet_run , args .silent_run , args .mode , args .exclude , args .version , args .variant , args .hap_setup_code )
0 commit comments