1010import os
1111from ipaddress import ip_address as ValidateIP , ip_network
1212import socket
13- import requests
1413import voluptuous as vol
1514import yaml
1615
1716import homeassistant .helpers .config_validation as cv
1817from homeassistant .components .sensor import PLATFORM_SCHEMA
1918from homeassistant .helpers .entity import Entity
2019
20+ from .providers import PROVIDERS
21+
2122_LOGGER = logging .getLogger (__name__ )
2223
2324CONF_NOTIFY = "enable_notification"
4142LOGFILE = "home-assistant.log"
4243OUTFILE = ".ip_authenticated.yaml"
4344
44- PROVIDERS = ["ipapi" , "extreme" , "ipvigilante" ]
45-
4645PLATFORM_SCHEMA = PLATFORM_SCHEMA .extend (
4746 {
48- vol .Optional (CONF_PROVIDER , default = "ipapi" ): vol .In (PROVIDERS ),
47+ vol .Optional (CONF_PROVIDER , default = "ipapi" ): vol .In (
48+ ["ipapi" , "extreme" , "ipvigilante" ]
49+ ),
4950 vol .Optional (CONF_LOG_LOCATION , default = "" ): cv .string ,
5051 vol .Optional (CONF_NOTIFY , default = True ): cv .boolean ,
5152 vol .Optional (CONF_EXCLUDE , default = []): vol .All (cv .ensure_list , [cv .string ]),
5253 }
5354)
5455
55- PROVIDERS = {}
5656
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" )
6260
6361
6462def setup_platform (hass , config , add_devices , discovery_info = None ):
@@ -78,14 +76,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
7876 add_devices ([sensor ], True )
7977
8078
81- class AuthenticatedBaseException (Exception ):
82- """Base exception for Authenticated."""
83-
84-
85- class AuthenticatedRateLimitException (AuthenticatedBaseException ):
86- """Ratelimit exception."""
87-
88-
8979class AuthenticatedSensor (Entity ):
9080 """Representation of a Sensor."""
9181
@@ -325,7 +315,7 @@ def load_authentications(authfile, exclude):
325315 if ValidateIP (token ["last_used_ip" ]) in ip_network (
326316 excludeaddress , False
327317 ):
328- raise Exception (' IP in excluded address configuration' )
318+ raise Exception (" IP in excluded address configuration" )
329319 if token .get ("last_used_at" ) is None :
330320 continue
331321 if token ["last_used_ip" ] in tokens_cleaned :
@@ -433,150 +423,3 @@ def notify(self, hass):
433423 last_used_at .replace ("T" , " " ),
434424 )
435425 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