#!/usr/bin/env python3 """ Check on esphome-devices """ import argparse import asyncio import logging import sys import common # import colorlog import zeroconf from aioesphomeapi import APIClient, APIConnectionError, ReconnectLogic, SensorState sleepsec = 60 noise_psk = common.env("el_esphome_api_psk") async def main(args): """Connect to an ESPHome device and get details.""" log.debug("function main()") # Establish connection api = APIClient( address=args.address, port=6053, password="", client_info="esphomeapi.py", noise_psk=noise_psk, ) log.debug("Connecting") try: await api.connect(login=True) except APIConnectionError as e: log.error("esphome api connection error - %s", e) sys.exit(1) log.info("Connected. Api version: " + str(api.api_version)) # Show device details log.debug("Getting device info") device = vars(await api.device_info()) log.info("Device name: " + device["name"]) log.debug("Getting sensors") rawsensors, _ = await api.list_entities_services() sensors = {} for sensor in rawsensors: sensors[sensor.key] = vars(sensor) if log.isEnabledFor(logging.DEBUG): from pprint import pformat log.debug("Sensors: \n" + pformat(sensors)) log.info("Disconnecting") await api.disconnect() def callback(state): if type(state) == SensorState and state.missing_state is False: log.debug("function callback(" + str(state) + ")") sensor = sensors[state.key] value = state.state if "accuracy_decimals" in sensor: decimals = sensor["accuracy_decimals"] value = round(value, decimals) if decimals > 0 else round(value) unit = ( sensor["unit_of_measurement"] if "unit_of_measurement" in sensor else "" ) device_class = sensor["device_class"] if "device_class" in sensor else "" log.info( sensor["name"] + " " + sensor["device_class"] + " - " + str(value) + str(unit) ) common.statein( device["friendly_name"], value, device_class, unit, verbose=True ) async def on_connect() -> None: log.debug("function on_connect()") log.info("Connected to API") try: await api.subscribe_states(callback) except APIConnectionError as e: log.error("esphome api connection error - %s", e) await api.disconnect() except Exception as e: log.error("catched exception - %s", e) await api.disconnect() async def on_disconnect() -> None: log.debug("function on_disconnect()") log.warning("Disconnected from API") await asyncio.sleep(sleepsec) reconnect = ReconnectLogic( client=api, on_connect=on_connect, on_disconnect=on_disconnect, zeroconf_instance=zeroconf.Zeroconf(), ) await reconnect.start() try: while True: try: log.debug("Sleep for " + str(sleepsec) + "s") await asyncio.sleep(sleepsec) except Exception as e: log.error("catched exception - %s", e) except KeyboardInterrupt: log.error("Keyboard interrupt") pass except KeyboardInterrupt: log.error("Keyboard interrupt") await reconnect.stop() finally: log.info("Closing loop.") if __name__ == "__main__": # Logging # handler = colorlog.StreamHandler() # handler.setFormatter(colorlog.ColoredFormatter( # "%(log_color)s%(levelname)s - %(message)s {%(filename)s:%(lineno)d}", # log_colors={ # 'DEBUG': 'light_black', # 'INFO': 'cyan', # 'WARNING': 'yellow', # 'ERROR': 'red', # 'CRITICAL': 'red,bg_white' # })) # log = colorlog.getLogger(__name__) # log.setLevel(logging.WARNING) # log.addHandler(handler) log = logging.getLogger(__name__) log.setLevel(logging.WARNING) logging.basicConfig(format="%(levelname)s - %(message)s {%(filename)s:%(lineno)d}") parser = argparse.ArgumentParser( description="Connect to an esphome-device and access the native api" ) parser.add_argument("-a", "--address", help="Address of esp-device to connect to") parser.add_argument( "-v", "--verbose", help="Set logging to debug mode", action="store_true" ) args = parser.parse_args() # Verbose logging? if args.verbose: log.setLevel(logging.DEBUG) log.debug("asyncio.run(main(args))") asyncio.run(main(args)) print("Bottom of script. Exiting.") sys.exit(0)