def blocking_login(self): def token_updater(token): asyncio.run_coroutine_threadsafe(self.token_updated(token), self.loop).result() if self._token != "": self._auth = Auth(self._ridentifier + "/1.0", self._token, token_updater) else: if self._password != "": self._auth = Auth(self._ridentifier + "/1.0", None, token_updater) if self._2facode: self._auth.fetch_token(self._username, self._password, self._2facode) else: try: self._auth.fetch_token(self._username, self._password) except AccessDeniedError: return "AccessDenied: wrong username or password" except MissingTokenError: return "please set 2fa_code" else: return "please set password" self._ring = Ring(self._auth)
def ring(mock_ring_requests): """Return ring object.""" auth = Auth("PythonRingDoorbell/0.6") auth.fetch_token("foo", "bar") ring = Ring(auth) ring.update_data() return ring
def initialize_ring(): cache_file = Path("token.cache") def token_updated(token): cache_file.write_text(json.dumps(token)) def otp_callback(): auth_code = input("2FA code: ") return auth_code if cache_file.is_file(): auth = Auth("CamBot/1.0", json.loads(cache_file.read_text()), token_updated) else: username = input("Username: "******"Password: "******"CamBot/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() return ring
def main(): if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = input("Username: "******"Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() devices = ring.devices() print(devices) doorbells = devices["doorbots"] chimes = devices["chimes"] stickup_cams = devices["stickup_cams"] print(doorbells) print(chimes) print(stickup_cams)
def ringOperations(emailname): s3 = boto3.client('s3') getCacheFileFromBucket(BUCKET_NAME, emailname) if cache_file.is_file(): cachefile = open(CACHE_FILE_NAME, "r") tokendata = json.loads(cachefile.read()) cachefile.close() auth = Auth("MyProject/1.0", tokendata, token_updated) uploadCacheFileToBucket(BUCKET_NAME, CACHE_FILE_NAME, emailname) ring = Ring(auth) ring.update_data() devices = ring.devices() getLatestMotionVideo(devices) getFrameFromVideo() sourceKey = "video_frame.png" imagelabels = getlabels(BUCKET_NAME, sourceKey) imagefaces = getfaces(BUCKET_NAME, sourceKey) imagetext = gettext(BUCKET_NAME, sourceKey) addImageInfotoTable(imagelabels, imagetext, imagefaces) s3.delete_object(Bucket=BUCKET_NAME, Key=sourceKey) s3.delete_object(Bucket=BUCKET_NAME, Key="last_trigger.mp4")
def main(argv): username = '' passwd = '' token_updated = None opts, args = getopt.getopt(argv, "u:p:") for opt, arg in opts: if opt in ("-u"): username = arg elif opt in ("-p"): passwd = arg auth = Auth(None, token_updated) auth.fetch_token(username, passwd) myring = Ring(auth) for dev in list(myring.doorbells): dev.update() print(dev.id + '||' + dev.family + '||' + dev.name)
def main(argv): username = '' passwd = '' token_updated = None opts, args = getopt.getopt(argv,"u:p:") for opt, arg in opts: if opt in ("-u"): username = arg elif opt in ("-p"): passwd = arg auth = Auth(None, token_updated) auth.fetch_token(username, passwd) myring = Ring(auth) for doorbell in list(myring.doorbells): doorbell.update() for event in doorbell.history(limit=20): print(str(doorbell.id)+'||'+str(event['id'])+'||'+str(event['kind'])+'||'+str(event['answered'])+'||'+str(event['created_at']))
def __init__(self): if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = input("Username: "******"Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) self.ring = Ring(auth) self.ring.update_data()
def login(): username = input("Username: "******"Password: "******"SmartThingsApi/0.1", token_updater=token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback())
def initialize_ring(): if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: auth = Auth("MyProject/1.0", None, token_updated) try: auth.fetch_token(config.ringuser, config.ringpassword) except MissingTokenError: auth.fetch_token(config.ringuser, config.ringpassword, otp_callback()) ring = Ring(auth) ring.update_data() return ring
def main(download_only=False): if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = os.environ.get('USERNAME') password = os.environ.get('PASSWORD') auth = Auth("MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() wait_for_update(ring, download_only=download_only)
async def validate_input(hass: core.HomeAssistant, data): """Validate the user input allows us to connect.""" auth = Auth(f"HomeAssistant/{const.__version__}") try: token = await hass.async_add_executor_job( auth.fetch_token, data["username"], data["password"], data.get("2fa"), ) except MissingTokenError: raise Require2FA except AccessDeniedError: raise InvalidAuth return token
def main(): if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = input("Username: "******"Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() devices = ring.devices() print(devices) doorbells = devices["doorbots"] chimes = devices["chimes"] stickup_cams = devices["stickup_cams"] print(doorbells) #print(chimes) #print(stickup_cams) x = 0 y = 0 limit = 100 doorbell = devices['doorbots'][0] typelist = { "motion", "ding", } for rt in typelist: print(rt) for i in doorbell.history(limit=limit, kind=rt): print(i['id']) doorbell.recording_download( doorbell.history(limit=limit, kind=rt)[x]['id'], filename=str(i['created_at'].strftime("%m-%d-%Y-%H-%M-%S")) + '.mp4', override=True) x += 1
def writeAuthFile(): logging.warn(f"will create new oauth json file at {config('OAUTH_FILE')}") username = input("Please enter your ring Username: "******"Please enter your ring Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback())
def main(): if cache_file.is_file(): print("Token has already been set up, loading...") auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = my_email print( "This is the first time setting up, getting the token set up now..." ) password = getpass.getpass("Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data()
async def validate_input(opp: core.OpenPeerPower, data): """Validate the user input allows us to connect.""" auth = Auth(f"OpenPeerPower/{const.__version__}") try: token = await opp.async_add_executor_job( auth.fetch_token, data["username"], data["password"], data.get("2fa"), ) except MissingTokenError as err: raise Require2FA from err except AccessDeniedError as err: raise InvalidAuth from err return token
def main(): global cache_file, token_updated, ring if not cache_file.is_file(): print("Token not found", file=sys.stderr) exit(1) print("Instantiating ring api...") auth = Auth(user_agent="SmartThingsApi/0.1", token=json.loads(cache_file.read_text()), token_updater=token_updated) ring = Ring(auth) ring.update_data() print("Instantiating background job...") scheduler = BackgroundScheduler() scheduler.add_job(pingring, 'interval', hours=2) scheduler.start() print("Starting web server...") run()
def main(): # initialize logger for debugging initialize_logger() if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: # initialize ring account username and password username = config('user') password = config('pw') # use the Authenticator of the ring_doorbell API # tries to fetch token for authentication # requests user input for the code if necessary auth = Auth("MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) threshold = input( "Enter the percentage where-in you want to get reminders: ") # loop for checking battery life while True: ring = Ring(auth) ring.update_data() # filter the Ring Devices devices = ring.devices() front_door = devices['authorized_doorbots'] battery_life = front_door[0].battery_life logging.info(f'The current battery life is {battery_life}') # if battery is less than threshold, send the e-mail if (battery_life <= int(threshold)): logging.info("Sending the email") send_email(battery_life) # loop sleeps for 6 hours 21600 sleep(3600)
def main(): if cache_file.is_file(): auth = Auth( "HomeAssistant/0.105.0dev0", json.loads(cache_file.read_text()), token_updated, ) else: username = input("Username: "******"Password: "******"Hello {ring.session['profile']['first_name']}") print() pprint(ring.devices_data)
def main(): if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = input("Username: "******"Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() devices = ring.devices() pprint(devices) # play with the API to figure out which camera you want deck = devices['doorbots'][0] download(deck) print('\nDONE.')
def main(): if cache_file.is_file(): token = json.loads(cache_file.read_text()) auth = Auth("Ringer/1.0", token, token_updated) print(f"Token expires at {expires_at_to_datetime(token['expires_at'])}") else: username = input("Username: "******"Password: "******"Ringer/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() devices = ring.devices() pprint(devices) doorbell = devices['doorbots'][0] history = doorbell.history(limit=100, kind='motion') id = history[0]['id'] doorbell.recording_download( id, filename=f'doorbell_motion_{id}.mp4', override=True) cams = devices['stickup_cams'] for cam in cams: history = cam.history(limit=100, kind='motion') id = history[0]['id'] cam.recording_download( id, filename=f'cam_motion_{id}.mp4', override=True)
# Connect to Hue Bridge b = Bridge(data['HUE_IP']) # First time need to press the button on the Bridge and connect (within 30 seconds) # b.connect() lights = [] hue_lights = b.lights print("[Hue] Connected") # Sign in to Ring RING_USERNAME = data['RING_USER'] RING_PASSWORD = data['RING_PASS'] auth = Auth("huebell/v1") auth.fetch_token(RING_USERNAME, RING_PASSWORD) ring = Ring(auth) ring.update_data() print("[Ring] Connected") # Load doorbell devices = ring.devices() doorbell = devices['doorbots'][0] def flash_lights(): # Get current status of all lights for l in hue_lights: light = { 'name': l.name,
async def async_setup_entry(hass, entry): """Set up a config entry.""" def token_updater(token): """Handle from sync context when token is updated.""" run_callback_threadsafe( hass.loop, partial( hass.config_entries.async_update_entry, entry, data={ **entry.data, "token": token }, ), ).result() auth = Auth(f"HomeAssistant/{__version__}", entry.data["token"], token_updater) ring = Ring(auth) try: await hass.async_add_executor_job(ring.update_data) except AccessDeniedError: _LOGGER.error( "Access token is no longer valid. Please set up Ring again") return False hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { "api": ring, "devices": ring.devices(), "device_data": GlobalDataUpdater(hass, "device", entry.entry_id, ring, "update_devices", timedelta(minutes=1)), "dings_data": GlobalDataUpdater( hass, "active dings", entry.entry_id, ring, "update_dings", timedelta(seconds=5), ), "history_data": DeviceDataUpdater( hass, "history", entry.entry_id, ring, lambda device: device.history(limit=10), timedelta(minutes=1), ), "health_data": DeviceDataUpdater( hass, "health", entry.entry_id, ring, lambda device: device.update_health_data(), timedelta(minutes=1), ), } hass.config_entries.async_setup_platforms(entry, PLATFORMS) if hass.services.has_service(DOMAIN, "update"): return True async def async_refresh_all(_): """Refresh all ring data.""" for info in hass.data[DOMAIN].values(): await info["device_data"].async_refresh_all() await info["dings_data"].async_refresh_all() await hass.async_add_executor_job(info["history_data"].refresh_all) await hass.async_add_executor_job(info["health_data"].refresh_all) # register service hass.services.async_register(DOMAIN, "update", async_refresh_all) return True
def get_access_token(): auth = Auth("PyRing/1.0.0", None, update_token) if not os.path.exists(token_file): try: print(' --- Get Ring Access Token --- \n') try: email = str( input(style.YELLOW('[+]') + style.RESET(f' Ring Email: '))) except KeyboardInterrupt: print(u"{}[2J{}[;H".format(chr(27), chr(27))) print(style.RED('\n[!]') + style.RESET(' Error: User exit.')) print( style.GREEN('[+]') + style.RESET(' Thank you for using PyRing.')) print(style.GREEN('[+]') + style.RESET(' Author: Pr0xy07')) print( style.GREEN('[+]') + style.RESET( ' If you need any help, contact: [email protected]') ) sys.exit(0) try: password = str( getpass.getpass( style.YELLOW('[+]') + style.RESET(f' Ring Password: '******'\n[!]') + style.RESET(' Error: User exit.')) print( style.GREEN('[+]') + style.RESET(' Thank you for using PyRing.')) print(style.GREEN('[+]') + style.RESET(' Author: Pr0xy07')) print( style.GREEN('[+]') + style.RESET( ' If you need any help, contact: [email protected]') ) sys.exit(0) auth.fetch_token(email, password) get_access_token.auth = Auth("PyRing/1.0.0", json.loads(token_file.read_text()), update_token) except MissingTokenError: auth.fetch_token(email, password, two_factor_authentication(email)) get_access_token.auth = Auth("PyRing/1.0.0", json.loads(token_file.read_text()), update_token) except: print(u"{}[2J{}[;H".format(chr(27), chr(27))) print( style.RED('\n[!]') + style.RESET( ' Cannot log in with the provided credentials, exiting...') ) print( style.GREEN('[+]') + style.RESET(' Thank you for using PyRing.')) print(style.GREEN('[+]') + style.RESET(' Author: Pr0xy07')) print( style.GREEN('[+]') + style.RESET( ' If you need any help, contact: [email protected]')) sys.exit(0) else: get_access_token.auth = Auth("PyRing/1.0.0", json.loads(token_file.read_text()), update_token)
logger.addHandler(ch) logger.addHandler(fh) # Connecting to RING.com def token_updated(token): cache_file.write_text(json.dumps(token)) def otp_callback(): auth_code = input("2FA code: ") return auth_code if cache_file.is_file(): auth = Auth("MyProject/1.0", json.loads(cache_file.read_text()), token_updated) else: username = input("Username: "******"Password: "******"MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) myring = Ring(auth) myring.update_data() fh = fhem.Fhem(fhem_ip, fhem_port)
def main(): parser = argparse.ArgumentParser( description="Ring Doorbell", epilog="https://github.com/tchellomello/python-ring-doorbell", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( "-u", "--username", dest="username", type=str, help="username for Ring account" ) parser.add_argument( "-p", "--password", type=str, dest="password", help="username for Ring account" ) parser.add_argument( "--count", action="store_true", default=False, help="count the number of videos on your Ring account", ) parser.add_argument( "--download-all", action="store_true", default=False, help="download all videos on your Ring account", ) args = parser.parse_args() _header() # connect to Ring account if cache_file.is_file(): auth = Auth("RingCLI/0.6", json.loads(cache_file.read_text()), token_updated) else: if not args.username: args.username = input("Username: "******"Password: "******"RingCLI/0.6", None, token_updated) try: auth.fetch_token(args.username, args.password) except MissingTokenError: auth.fetch_token(args.username, args.password, input("2FA Code: ")) ring = Ring(auth) ring.update_data() devices = ring.devices() doorbell = devices["doorbots"][0] _bar() if args.count: print( "\tCounting videos linked on your Ring account.\n" + "\tThis may take some time....\n" ) events = [] counter = 0 history = doorbell.history(limit=100) while len(history) > 0: events += history counter += len(history) history = doorbell.history(older_than=history[-1]["id"]) motion = len([m["kind"] for m in events if m["kind"] == "motion"]) ding = len([m["kind"] for m in events if m["kind"] == "ding"]) on_demand = len([m["kind"] for m in events if m["kind"] == "on_demand"]) print("\tTotal videos: {}".format(counter)) print("\tDing triggered: {}".format(ding)) print("\tMotion triggered: {}".format(motion)) print("\tOn-Demand triggered: {}".format(on_demand)) # already have all events in memory if args.download_all: counter = 0 print( "\tDownloading all videos linked on your Ring account.\n" + "\tThis may take some time....\n" ) for event in events: counter += 1 filename = _format_filename(event) print("\t{}/{} Downloading {}".format(counter, len(events), filename)) doorbell.recording_download( event["id"], filename=filename, override=False ) if args.download_all and not args.count: print( "\tDownloading all videos linked on your Ring account.\n" + "\tThis may take some time....\n" ) history = doorbell.history(limit=100) while len(history) > 0: print( "\tProcessing and downloading the next" + " videos".format(len(history)) ) counter = 0 for event in history: counter += 1 filename = _format_filename(event) print("\t{}/{} Downloading {}".format(counter, len(history), filename)) doorbell.recording_download( event["id"], filename=filename, override=False ) history = doorbell.history(limit=100, older_than=history[-1]["id"])
def main(): if cache_file.is_file(): auth = Auth("MyProject/1.0d", json.loads(cache_file.read_text()), token_updated) else: # Comment or Un the following to be prompted or use what we have already set # username = input("Username: "******"Password: "******"MyProjectShared/1.0d", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) myring = Ring(auth) myring.update_data() #;print(How To Print All Data) devices = myring.devices() #;print(devices) doorbells = devices["doorbots"] print(doorbells) stickup_cams = devices["stickup_cams"] #;print(stickup_cams) chimes = devices["chimes"] #;print(chimes) # print( "myring.is_connected" ), # Not valid after python2.7 # print( "myring.is_connected", end = '' ) # print( myring.is_connected ) # True ## Would be nice to esplicitly test that we got connefcted, # p = subprocess.Popen(["sleep", "1"], stdout=subprocess.PIPE); output, err = p.communicate()#; print(output.rstrip(os.linesep)) # Sleep for BreakAbility # p = subprocess.Popen(["sleep", "1"], stdout=subprocess.PIPE); output, err = p.communicate()#; print(output.rstrip(os.linesep)) # Sleep for BreakAbility for acamera in list(stickup_cams + doorbells): # limit on dev.account_id = 12345678 and print the Account ID: when connecting? # for event in acamera.history(limit=QueueDepth, older_than=6753104150123456789): # ['created_at'] #,'id'] # Older than example for event in acamera.history( limit=QueueDepth, older_than=OlderThan): # ['created_at'] #,'id'] # filename='%s-%s-%s' % (acamera.name.replace(" ","_"), event['created_at'].astimezone(timezone('US/Pacific')).strftime("%Y%m%d.%H%M%S"), event['id']) # from pytz import timezone # TODO: ... ... Need a new Ignore: state for cameras that match "-Driveway-" for the neighbors cam. # #Or filter on the Account ID: 12345678 camname = '%s' % acamera.name.replace( " ", "_") # Sanitize Cam Names, of Spaces at least... ... cliptime = '%s' % event['created_at'].astimezone( timezone(MyTimeZone)).strftime("%Y%m%d.%H%M%S") clipyear = '%s' % event['created_at'].astimezone( timezone(MyTimeZone)).strftime("%Y") clipmonth = '%s' % event['created_at'].astimezone( timezone(MyTimeZone)).strftime("%m") clipday = '%s' % event['created_at'].astimezone( timezone(MyTimeZone)).strftime("%d") clipid = '%s' % event['id'] foldername = BaseDir + "/" + clipyear + "/" + clipmonth + "/" + clipday p = subprocess.Popen(["mkdir", "-pv", foldername], stdout=subprocess.PIPE) output, err = p.communicate() # print(output.rstrip(os.linesep)) # print the result of creating the container folder YYYY/MM/DD # Prints an empty line if dir already exists filename = cliptime + "-" + camname + "-" + clipid #print(filename) filepath = foldername + "/" + filename + ".mp4" print(bcolors.OKBLUE + "Fetchin " + filepath + bcolors.ENDC, end='') sys.stdout.flush() # import sys # Force partial line to be printed print( "\r", end='' ) # Carrage return back to top of line for the results to be rewritten if not os.path.exists( filepath): # import os # Test that file does not exist yet ## Would like to esplicitly watch for a ^C BREAK from the parent, sometimes that gets ignored and can not abort. if acamera.recording_download(event['id'], filename=filepath): print(bcolors.OKGREEN + "Success" + bcolors.ENDC + bcolors.BOLD, end='') # subprocess.call(["ls", "-lh", filepath]) # import subprocess # # print(subprocess.call(["ls", "-lh", filepath])) # import subprocess # Prints ls output and then "Success 0"(Return Code)? p = subprocess.Popen(["ls", "-lh", filepath], stdout=subprocess.PIPE) output, err = p.communicate() # print("*** Running ls -l command ***\n") # print(output), # Does a new line even though we used a comma, otherwise does two new lines, must have a \n within the output # print(output.rstrip(os.linesep)) # This inline comment no longer works from py2.7 to py3.6 # similar to perl chomp to cleanup /r/n # df=os.system("df -h / | grep /") # Not working right? # print(df) # when attempting .rstrip(os.linesep) get error AttributeError: 'int' object has no attribute 'rstrip' print(bcolors.UNDERLINE + bcolors.OKGREEN + "#" + bcolors.ENDC) # print(acamera.recording_url(event['id'])) # Could print this into a filename.lnk instead to keep a shareable link? DEBUG else: # acamera.recording_download failed print(bcolors.FAIL + "Failed-" + bcolors.ENDC) print(acamera.recording_url(event['id'])) else: # os.path.exists so skip print(bcolors.WARNING + "Skipped" + bcolors.ENDC)
def main(): configure_logger() register_stack_dump() global slack slack = Slacker(SLACK_API_KEY) global worker_thread worker_thread = Thread(target=worker_loop, name="Worker Thread") worker_thread.daemon = True worker_thread.start() global s3_client s3_client = boto3.client( 's3', aws_access_key_id=AWS_ACCESS_KEY, aws_secret_access_key=AWS_SECREY_KEY, ) logger.info("Connecting to Ring API") if cache_file.is_file(): auth = Auth("MyProject/1.0", loads(cache_file.read_text()), token_updated) else: username = RING_USERNAME password = RING_PASSWORD auth = Auth("MyProject/1.0", None, token_updated) try: auth.fetch_token(username, password) except MissingTokenError: auth.fetch_token(username, password, otp_callback()) ring = Ring(auth) ring.update_data() logger.info("Connected to Ring API") devices = ring.devices() logger.info("Found devices {}".format(devices)) video_devices = devices["doorbots"] + devices["stickup_cams"] logger.info("Watching for events on devices {}".format(video_devices)) # Init event ids for device in video_devices: device.latest_id = get_latest_recording(device)["id"] logger.info("Found latest event id {} for device {}".format( device.latest_id, device)) # Loop for ever, checking to see if a new video becomes available try: while True: time.sleep(HIST_POLL_TIMEOUT_SECS) for device in video_devices: event = get_latest_recording(device) new_id = event["id"] if new_id != device.latest_id: logger.info("Detected a new event for device {}".format(device)) device.latest_id = new_id enqueue_event(event, device) except KeyboardInterrupt: exit(0)
def getAuth(): if oauth_file.is_file(): auth = Auth("MyProject/1.0", json.loads(oauth_file.read_text()), token_updated) else: sys.exit(f"Authorization file does not exist {oauthFilePath}") return auth
class ring(FhemModule): def __init__(self, logger): super().__init__(logger) self.loop = asyncio.get_event_loop() self._username = None self._password = "" self._token = "" self._2facode = None self._attr_dingPollInterval = 5 self._attr_deviceUpdateInterval = 300 self._history = [] self._rdevice = None self._lastrecording_url = "" self._livestreamjson = "" self._login_lock = asyncio.Lock() self._snapshot = None self._attr_list = { "deviceUpdateInterval": { "default": 300, "format": "int" }, "dingPollInterval": { "default": 2, "format": "int" }, } self.set_attr_config(self._attr_list) self.set_list_conf = { "password": { "args": ["password"] }, "2fa_code": { "args": ["2facode"] }, } self.set_set_config(self.set_list_conf) self.stop_dings_loop = threading.Event() self.update_dings_thread = None return async def token_updated(self, token): self._token = token encrypted_token = utils.encrypt_string(json.dumps(token), self._reading_encryption_key) await fhem.readingsSingleUpdate(self.hash, "token", encrypted_token, 1) # FHEM FUNCTION async def Define(self, hash, args, argsh): await super().Define(hash, args, argsh) if len(args) < 5: return ("Usage: define rrring fhempy ring <USERNAME>" " <RING_DEVICE_NAME> [<IDENTIFIER>]") self._username = args[3] self._rdevname = args[4] if len(args) > 5: self._ridentifier = args[5] self.hash["IDENTIFIER"] = args[5] else: characters = string.ascii_letters + string.digits self._ridentifier = "".join( random.choice(characters) for _ in range(20)) self._reading_encryption_key = await fhem.getUniqueId(hash) # ring service self._ring = None # ring doorbell/chime/... device self._rdevice = None self.hash["USERNAME"] = args[3] self.hash["RINGDEVICE"] = args[4] self.create_async_task(self.ring_login()) async def ring_login(self): async with self._login_lock: if self._token == "": token_reading = await fhem.ReadingsVal(self.hash["NAME"], "token", "") if token_reading != "": token_reading = utils.decrypt_string( token_reading, self._reading_encryption_key) self._token = json.loads(token_reading) if self._password == "": self._password = await fhem.ReadingsVal( self.hash["NAME"], "password", "") if self._password != "": self._password = utils.decrypt_string( self._password, self._reading_encryption_key) try: ret = await utils.run_blocking( functools.partial(self.blocking_login)) if ret: await fhem.readingsSingleUpdate(self.hash, "state", ret, 1) return await fhem.readingsSingleUpdate(self.hash, "state", "connected", 1) # start update loop, we are already in a task, therefore no need to create await self.update_loop() except Exception: await fhem.readingsSingleUpdate(self.hash, "state", "Login failed", 1) self.logger.error("Login failed") async def update_loop(self): try: await utils.run_blocking(functools.partial(self._ring.update_data)) devices = await utils.run_blocking( functools.partial(self._ring.devices)) for dev_type in devices: for dev in devices[dev_type]: if dev.name == self._rdevname: self._rdevice = dev if self._rdevice is None: await fhem.readingsSingleUpdate(self.hash, "state", "device not found", 1) return if self._rdevice is not None and self._rdevice.has_capability( "volume"): self.set_list_conf["volume"] = { "args": ["volume"], "params": { "volume": { "format": "int" } }, "options": "slider,0,1,10", } self.set_set_config(self.set_list_conf) await self.update_readings() self.create_async_task(self.update_dings_loop()) while True: try: await utils.run_blocking( functools.partial(self.poll_device)) await self.update_readings() # handle history if len(self._history) > 0: i = 1 for event in self._history: await self.update_history_readings(event, i) i += 1 except Exception: self.logger.exception("Failed to poll devices") await asyncio.sleep(self._attr_deviceUpdateInterval) except CancelledError: pass except Exception: self.logger.exception("Failed to update devices") async def update_dings_loop(self): try: self.update_dings_thread = await utils.run_blocking( functools.partial(self.update_dings_loop_thread)) except CancelledError: pass def update_dings_loop_thread(self): alert_active = 0 while True: try: if self.stop_dings_loop.is_set(): return self.poll_dings() # handle alerts alerts = self._ring.active_alerts() self.logger.debug("Received dings: " + str(alerts)) if len(alerts) > 0: for alert in alerts: if alert["doorbot_id"] == self._rdevice.id: alert_active = 1 asyncio.run_coroutine_threadsafe( self.update_alert_readings(alert), self.loop).result() elif alert_active == 1: alert_active = 0 asyncio.run_coroutine_threadsafe( fhem.readingsSingleUpdateIfChanged( self.hash, "state", "connected", 1), self.loop, ).result() self.poll_device() asyncio.run_coroutine_threadsafe(self.update_readings(), self.loop).result() except Exception: self.logger.exception("Failed to poll dings...") self.blocking_login() time.sleep(self._attr_dingPollInterval) async def update_alert_readings(self, alert): await fhem.readingsBeginUpdate(self.hash) await fhem.readingsBulkUpdate(self.hash, "alert_id", alert["id"]) await fhem.readingsBulkUpdate(self.hash, "alert_kind", alert["kind"]) await fhem.readingsBulkUpdate(self.hash, "alert_sip_to", alert["sip_to"]) await fhem.readingsBulkUpdate(self.hash, "alert_sip_token", alert["sip_token"]) await fhem.readingsBulkUpdate(self.hash, "alert_doorbot_id", alert["doorbot_id"]) await fhem.readingsBulkUpdate(self.hash, "state", alert["kind"]) await fhem.readingsEndUpdate(self.hash, 1) async def update_history_readings(self, event, idx): await fhem.readingsBeginUpdate(self.hash) await fhem.readingsBulkUpdateIfChanged(self.hash, "history_" + str(idx) + "_id", event["id"]) await fhem.readingsBulkUpdateIfChanged(self.hash, "history_" + str(idx) + "_kind", event["kind"]) await fhem.readingsBulkUpdateIfChanged( self.hash, "history_" + str(idx) + "_answered", event["answered"]) await fhem.readingsBulkUpdateIfChanged( self.hash, "history_" + str(idx) + "_created_at", event["created_at"]) await fhem.readingsEndUpdate(self.hash, 1) async def update_readings(self): await fhem.readingsBeginUpdate(self.hash) try: await fhem.readingsBulkUpdateIfChanged(self.hash, "address", self._rdevice.address) await fhem.readingsBulkUpdateIfChanged(self.hash, "family", self._rdevice.family) await fhem.readingsBulkUpdateIfChanged(self.hash, "device_id", self._rdevice.device_id) await fhem.readingsBulkUpdateIfChanged(self.hash, "id", self._rdevice.id) await fhem.readingsBulkUpdateIfChanged(self.hash, "model", self._rdevice.model) await fhem.readingsBulkUpdateIfChanged(self.hash, "firmware", self._rdevice.firmware) await fhem.readingsBulkUpdateIfChanged(self.hash, "latitude", self._rdevice.latitude) await fhem.readingsBulkUpdateIfChanged(self.hash, "longitude", self._rdevice.longitude) await fhem.readingsBulkUpdateIfChanged(self.hash, "kind", self._rdevice.kind) await fhem.readingsBulkUpdateIfChanged(self.hash, "name", self._rdevice.name) await fhem.readingsBulkUpdateIfChanged(self.hash, "timezone", self._rdevice.timezone) await fhem.readingsBulkUpdateIfChanged(self.hash, "wifi_name", self._rdevice.wifi_name) await fhem.readingsBulkUpdateIfChanged( self.hash, "wifi_signal_strength", self._rdevice.wifi_signal_strength) await fhem.readingsBulkUpdateIfChanged( self.hash, "wifi_signal_category", self._rdevice.wifi_signal_category) await self.update_if_available("battery_life") await self.update_if_available("existing_doorbell_type") await self.update_if_available("existing_doorbell_type_enabled") await self.update_if_available("existing_doorbell_type_duration") await self.update_if_available("subscribed") await self.update_if_available("subscribed_motion") await self.update_if_available("has_subscription") await self.update_if_available("volume") await self.update_if_available("connection_status") if (self._rdevice.family == "doorbots" or self._rdevice.family == "authorized_doorbots"): await self.update_if_available("last_recording_id") await fhem.readingsBulkUpdateIfChanged(self.hash, "last_recording_url", self._lastrecording_url) if self._livestreamjson != "": await fhem.readingsBulkUpdateIfChanged( self.hash, "livestream_json", json.dumps(self._livestreamjson)) if self._snapshot: snapshot = ('<html><img src="data:image/png,' + self._snapshot + '"/></html>') await fhem.readingsBulkUpdateIfChanged( self.hash, "snapshot", snapshot) except Exception: self.logger.exception( "Failed to update readings, please report here: https://forum.fhem.de/index.php/topic,117381" ) await fhem.readingsEndUpdate(self.hash, 1) async def update_if_available(self, reading): if hasattr(self._rdevice, reading): await fhem.readingsBulkUpdateIfChanged( self.hash, reading, getattr(self._rdevice, reading)) def poll_dings(self): self._ring.update_dings() def poll_device(self): self._rdevice.update_health_data() self._ring.update_data() self._history = [] if (self._rdevice.family == "doorbots" or self._rdevice.family == "authorized_doorbots"): # disable it, as it creates a lot of history entries # self._livestreamjson = self._rdevice.live_streaming_json for event in self._rdevice.history(limit=5): self._history.append(event) self._lastrecording_url = self._rdevice.recording_url( self._rdevice.last_recording_id) if self._lastrecording_url is False: self.blocking_login() # self._snapshot = self._rdevice.get_snapshot(retries=10) def blocking_login(self): def token_updater(token): asyncio.run_coroutine_threadsafe(self.token_updated(token), self.loop).result() if self._token != "": self._auth = Auth(self._ridentifier + "/1.0", self._token, token_updater) else: if self._password != "": self._auth = Auth(self._ridentifier + "/1.0", None, token_updater) if self._2facode: self._auth.fetch_token(self._username, self._password, self._2facode) else: try: self._auth.fetch_token(self._username, self._password) except AccessDeniedError: return "AccessDenied: wrong username or password" except MissingTokenError: return "please set 2fa_code" else: return "please set password" self._ring = Ring(self._auth) async def set_volume(self, hash, params): new_vol = params["volume"] self.create_async_task(self.set_volume_task(new_vol)) async def set_volume_task(self, new_volume): await utils.run_blocking( functools.partial(self.set_volume_blocking, new_volume)) await self.update_readings() def set_volume_blocking(self, new_volume): self._rdevice.volume = new_volume async def set_password(self, hash, params): self._password = params["password"] encrypted_password = utils.encrypt_string(self._password, self._reading_encryption_key) await fhem.readingsSingleUpdateIfChanged(self.hash, "password", encrypted_password, 1) self.create_async_task(self.ring_login()) async def set_2fa_code(self, hash, params): self._2facode = params["2facode"] self.create_async_task(self.ring_login()) async def Undefine(self, hash): if self.update_dings_thread: self.update_dings_thread.cancel() self.stop_dings_loop.set() await super().Undefine(hash)