10
10
import os
11
11
from ipaddress import ip_address as ValidateIP , ip_network
12
12
import socket
13
- import requests
14
13
import voluptuous as vol
15
14
import yaml
16
15
17
16
import homeassistant .helpers .config_validation as cv
18
17
from homeassistant .components .sensor import PLATFORM_SCHEMA
19
18
from homeassistant .helpers .entity import Entity
20
19
20
+ from .providers import PROVIDERS
21
+
21
22
_LOGGER = logging .getLogger (__name__ )
22
23
23
24
CONF_NOTIFY = "enable_notification"
41
42
LOGFILE = "home-assistant.log"
42
43
OUTFILE = ".ip_authenticated.yaml"
43
44
44
- PROVIDERS = ["ipapi" , "extreme" , "ipvigilante" ]
45
-
46
45
PLATFORM_SCHEMA = PLATFORM_SCHEMA .extend (
47
46
{
48
- vol .Optional (CONF_PROVIDER , default = "ipapi" ): vol .In (PROVIDERS ),
47
+ vol .Optional (CONF_PROVIDER , default = "ipapi" ): vol .In (
48
+ ["ipapi" , "extreme" , "ipvigilante" ]
49
+ ),
49
50
vol .Optional (CONF_LOG_LOCATION , default = "" ): cv .string ,
50
51
vol .Optional (CONF_NOTIFY , default = True ): cv .boolean ,
51
52
vol .Optional (CONF_EXCLUDE , default = []): vol .All (cv .ensure_list , [cv .string ]),
52
53
}
53
54
)
54
55
55
- PROVIDERS = {}
56
56
57
-
58
- def register_provider (classname ):
59
- """Decorator used to register providers."""
60
- PROVIDERS [classname .name ] = classname
61
- return classname
57
+ def humanize_time (timestring ):
58
+ """Convert time."""
59
+ return datetime .strptime (timestring [:19 ], "%Y-%m-%dT%H:%M:%S" )
62
60
63
61
64
62
def setup_platform (hass , config , add_devices , discovery_info = None ):
@@ -78,14 +76,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
78
76
add_devices ([sensor ], True )
79
77
80
78
81
- class AuthenticatedBaseException (Exception ):
82
- """Base exception for Authenticated."""
83
-
84
-
85
- class AuthenticatedRateLimitException (AuthenticatedBaseException ):
86
- """Ratelimit exception."""
87
-
88
-
89
79
class AuthenticatedSensor (Entity ):
90
80
"""Representation of a Sensor."""
91
81
@@ -325,7 +315,7 @@ def load_authentications(authfile, exclude):
325
315
if ValidateIP (token ["last_used_ip" ]) in ip_network (
326
316
excludeaddress , False
327
317
):
328
- raise Exception (' IP in excluded address configuration' )
318
+ raise Exception (" IP in excluded address configuration" )
329
319
if token .get ("last_used_at" ) is None :
330
320
continue
331
321
if token ["last_used_ip" ] in tokens_cleaned :
@@ -433,150 +423,3 @@ def notify(self, hass):
433
423
last_used_at .replace ("T" , " " ),
434
424
)
435
425
notify (message , title = "New successful login" , notification_id = self .ip_address )
436
-
437
-
438
- class GeoProvider :
439
- """GeoProvider class."""
440
-
441
- url = None
442
-
443
- def __init__ (self , ipaddr ):
444
- """Initialize."""
445
- self .result = {}
446
- self .ipaddr = ipaddr
447
-
448
- @property
449
- def country (self ):
450
- """Return country name or None."""
451
- if self .result :
452
- if self .result .get ("country" ) is not None :
453
- return self .result ["country" ]
454
- return None
455
-
456
- @property
457
- def region (self ):
458
- """Return region name or None."""
459
- if self .result :
460
- if self .result .get ("region" ) is not None :
461
- return self .result ["region" ]
462
- return None
463
-
464
- @property
465
- def city (self ):
466
- """Return city name or None."""
467
- if self .result :
468
- if self .result .get ("city" ) is not None :
469
- return self .result ["city" ]
470
- return None
471
-
472
- @property
473
- def computed_result (self ):
474
- """Return the computed result."""
475
- if self .result is not None :
476
- return {"country" : self .country , "region" : self .region , "city" : self .city }
477
- return None
478
-
479
- def update_geo_info (self ):
480
- """Update Geo Information."""
481
- self .result = {}
482
- try :
483
- api = self .url .format (self .ipaddr )
484
- header = {"user-agent" : "Home Assistant/Python" }
485
- data = requests .get (api , headers = header , timeout = 5 ).json ()
486
-
487
- if data .get ("error" ):
488
- if data .get ("reason" ) == "RateLimited" :
489
- raise AuthenticatedRateLimitException (
490
- "RatelimitError, try a different provider."
491
- )
492
-
493
- elif data .get ("status" , "success" ) == "error" :
494
- return
495
-
496
- elif data .get ("reserved" ):
497
- return
498
-
499
- elif data .get ("status" , "success" ) == "fail" :
500
- raise AuthenticatedBaseException (
501
- "[{}] - {}" .format (
502
- self .ipaddr , data .get ("message" , "Unknown error." )
503
- )
504
- )
505
-
506
- self .result = data
507
- self .parse_data ()
508
- except AuthenticatedRateLimitException as exception :
509
- _LOGGER .error (exception )
510
- except AuthenticatedBaseException as exception :
511
- _LOGGER .error (exception )
512
- except requests .exceptions .ConnectionError :
513
- pass
514
-
515
- def parse_data (self ):
516
- """Parse data from geoprovider."""
517
- self .result = self .result
518
-
519
-
520
- @register_provider
521
- class IPApi (GeoProvider ):
522
- """IPApi class."""
523
-
524
- url = "https://ipapi.co/{}/json"
525
- name = "ipapi"
526
-
527
- @property
528
- def country (self ):
529
- """Return country name or None."""
530
- if self .result :
531
- if self .result .get ("country_name" ) is not None :
532
- return self .result ["country_name" ]
533
- return None
534
-
535
-
536
- @register_provider
537
- class ExtremeIPLookup (GeoProvider ):
538
- """IPApi class."""
539
-
540
- url = "https://extreme-ip-lookup.com/json/{}"
541
- name = "extreme"
542
-
543
-
544
- @register_provider
545
- class IPVigilante (GeoProvider ):
546
- """IPVigilante class."""
547
-
548
- url = "https://ipvigilante.com/json/{}"
549
- name = "ipvigilante"
550
-
551
- def parse_data (self ):
552
- """Parse data from geoprovider."""
553
- self .result = self .result .get ("data" , {})
554
-
555
- @property
556
- def country (self ):
557
- """Return country name or None."""
558
- if self .result :
559
- if self .result .get ("country_name" ) is not None :
560
- return self .result ["country_name" ]
561
- return None
562
-
563
- @property
564
- def region (self ):
565
- """Return region name or None."""
566
- if self .result :
567
- if self .result .get ("subdivision_1_name" ) is not None :
568
- return self .result ["subdivision_1_name" ]
569
- return None
570
-
571
- @property
572
- def city (self ):
573
- """Return city name or None."""
574
- if self .result :
575
- if self .result .get ("city_name" ) is not None :
576
- return self .result ["city_name" ]
577
- return None
578
-
579
-
580
- def humanize_time (timestring ):
581
- """Convert time."""
582
- return datetime .strptime (timestring [:19 ], "%Y-%m-%dT%H:%M:%S" )
0 commit comments