class Network(NetworkBase):
    """Class representing the Adafruit FunHouse.

    :param status_dotstar: The initialized object for status DotStar. Defaults to ``None``,
                           to not use the status LED
    :param bool extract_values: If true, single-length fetched values are automatically extracted
                                from lists and tuples. Defaults to ``True``.
    :param debug: Turn on debug print outs. Defaults to False.

    """

    # pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements
    def __init__(
        self,
        *,
        status_dotstar=None,
        extract_values=True,
        debug=False,
    ):
        super().__init__(
            WiFi(status_dotstar=status_dotstar),
            extract_values=extract_values,
            debug=debug,
        )
        self._mqtt_client = None

    def init_io_mqtt(self):
        """Initialize MQTT for Adafruit IO"""
        try:
            aio_username = self._secrets["aio_username"]
            aio_key = self._secrets["aio_key"]
        except KeyError:
            raise KeyError(
                "Adafruit IO secrets are kept in secrets.py, please add them there!\n\n"
            ) from KeyError

        return self.init_mqtt(IO_MQTT_BROKER, 8883, aio_username, aio_key, True)

    # pylint: disable=too-many-arguments
    def init_mqtt(
        self,
        broker,
        port=8883,
        username=None,
        password=None,
        use_io=False,
    ):
        """Initialize MQTT"""
        self.connect()
        self._mqtt_client = MQTT.MQTT(
            broker=broker,
            port=port,
            username=username,
            password=password,
            socket_pool=self._wifi.pool,
            ssl_context=ssl.create_default_context(),
        )
        if use_io:
            self._mqtt_client = IO_MQTT(self._mqtt_client)

        return self._mqtt_client

    # pylint: enable=too-many-arguments

    def _get_mqtt_client(self):
        print(self._mqtt_client)
        if self._mqtt_client is not None:
            return self._mqtt_client
        raise RuntimeError("Please initialize MQTT before using")

    def mqtt_loop(self, *args, suppress_mqtt_errors=True, **kwargs):
        """Run the MQTT Loop"""
        self._get_mqtt_client()
        if suppress_mqtt_errors:
            try:
                if self._mqtt_client is not None:
                    self._mqtt_client.loop(*args, **kwargs)
            except MQTT.MMQTTException as err:
                print("MMQTTException: {0}".format(err))
            except OSError as err:
                print("OSError: {0}".format(err))
        else:
            if self._mqtt_client is not None:
                self._mqtt_client.loop(*args, **kwargs)

    def mqtt_publish(self, *args, suppress_mqtt_errors=True, **kwargs):
        """Publish to MQTT"""
        self._get_mqtt_client()
        if suppress_mqtt_errors:
            try:
                if self._mqtt_client is not None:
                    self._mqtt_client.publish(*args, **kwargs)
            except OSError as err:
                print("OSError: {0}".format(err))
        else:
            if self._mqtt_client is not None:
                self._mqtt_client.publish(*args, **kwargs)

    def mqtt_connect(self, *args, **kwargs):
        """Connect to MQTT"""
        self._get_mqtt_client()
        if self._mqtt_client is not None:
            self._mqtt_client.connect(*args, **kwargs)

    @property
    def on_mqtt_connect(self):
        """
        Get or Set the MQTT Connect Handler

        """
        if self._mqtt_client:
            return self._mqtt_client.on_connect
        return None

    @on_mqtt_connect.setter
    def on_mqtt_connect(self, value):
        self._get_mqtt_client()
        self._mqtt_client.on_connect = value

    @property
    def on_mqtt_disconnect(self):
        """
        Get or Set the MQTT Disconnect Handler

        """
        if self._mqtt_client:
            return self._mqtt_client.on_disconnect
        return None

    @on_mqtt_disconnect.setter
    def on_mqtt_disconnect(self, value):
        self._get_mqtt_client().on_disconnect = value

    @property
    def on_mqtt_subscribe(self):
        """
        Get or Set the MQTT Subscribe Handler

        """
        if self._mqtt_client:
            return self._mqtt_client.on_subscribe
        return None

    @on_mqtt_subscribe.setter
    def on_mqtt_subscribe(self, value):
        self._get_mqtt_client().on_subscribe = value

    @property
    def on_mqtt_unsubscribe(self):
        """
        Get or Set the MQTT Unsubscribe Handler

        """
        if self._mqtt_client:
            return self._mqtt_client.on_unsubscribe
        return None

    @on_mqtt_unsubscribe.setter
    def on_mqtt_unsubscribe(self, value):
        self._get_mqtt_client().on_unsubscribe = value

    @property
    def on_mqtt_message(self):
        """
        Get or Set the MQTT Message Handler

        """
        if self._mqtt_client:
            return self._mqtt_client.on_message
        return None

    @on_mqtt_message.setter
    def on_mqtt_message(self, value):
        self._get_mqtt_client().on_message = value

    @property
    def enabled(self):
        """
        Return whether the WiFi is enabled

        """
        return self._wifi.enabled
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
    username=secrets["aio_username"],
    password=secrets["aio_key"],
)

# Initialize an Adafruit IO MQTT Client
io = IO_MQTT(mqtt_client)

# Connect the callback methods defined above to Adafruit IO
io.on_connect = connected
io.on_disconnect = disconnected
io.on_message = message

# Connect to Adafruit IO
io.connect()

# Start a blocking message loop...
# NOTE: NO code below this loop will execute
# NOTE: Network reconnection is handled within this loop
while True:
    try:
        io.loop()
    except (ValueError, RuntimeError) as e:
        print("Failed to get data, retrying\n", e)
        wifi.reset()
        io.reconnect()
        continue
    time.sleep(1)
Пример #3
0
class DataWarehouse():
    SECRETS_REQUIRED = ("ssid", "password", "aio_username", "aio_key")

    def __init__(
            self,
            secrets_,
            *,
            esp01_pins=[],
            esp01_uart=None,
            esp01_baud=115200,
            pub_prefix="",
            debug=False  ### pylint: disable=redefined-outer-name
    ):

        if esp01_uart:
            self.esp01_uart = esp01_uart
        else:
            self.esp01_uart = busio.UART(*esp01_pins,
                                         receiver_buffer_size=2048)

        self.debug = debug
        self.esp = adafruit_espatcontrol.ESP_ATcontrol(self.esp01_uart,
                                                       esp01_baud,
                                                       debug=debug)
        self.esp_version = ""
        self.wifi = None
        try:
            _ = [secrets_[key] for key in self.SECRETS_REQUIRED]
        except KeyError:
            raise RuntimeError("secrets.py must contain: " +
                               " ".join(self.SECRETS_REQUIRED))
        self.secrets = secrets_
        self.io = None
        self.pub_prefix = pub_prefix
        self.pub_name = {}
        self.init_connect()

    def init_connect(self):
        self.esp.soft_reset()

        self.wifi = adafruit_espatcontrol_wifimanager.ESPAT_WiFiManager(
            self.esp, self.secrets)
        ### A few retries here seems to greatly improve reliability
        for _ in range(4):
            if self.debug:
                print("Connecting to WiFi...")
            try:
                self.wifi.connect()
                self.esp_version = self.esp.get_version()
                if self.debug:
                    print("Connected!")
                break
            except (RuntimeError, TypeError,
                    adafruit_espatcontrol.OKError) as ex:
                if self.debug:
                    print("EXCEPTION: Failed to publish()", repr(ex))
                time.sleep(RECONNECT_SLEEP)

        ### This uses global variables
        socket.set_interface(self.esp)

        ### MQTT Client
        ### pylint: disable=protected-access
        self.mqtt_client = MQTT.MQTT(broker="io.adafruit.com",
                                     username=self.secrets["aio_username"],
                                     password=self.secrets["aio_key"],
                                     socket_pool=socket,
                                     ssl_context=MQTT._FakeSSLContext(
                                         self.esp))
        self.io = IO_MQTT(self.mqtt_client)
        ### Callbacks of interest on io are
        ### on_connect on_disconnect on_subscribe
        self.io.connect()

    def reset_and_reconnect(self):
        self.wifi.reset()
        self.io.reconnect()

    def update_pub_name(self, field_name):
        pub_name = self.pub_prefix + field_name
        return pub_name

    def poll(self):
        dw_poll_ok = False
        try_reconnect = False
        for _ in range(2):
            try:
                ### Process any incoming messages
                if try_reconnect:
                    self.reset_and_reconnect()
                    try_reconnect = False
                self.io.loop()
                dw_poll_ok = True
            except (ValueError, RuntimeError, AttributeError,
                    MQTT.MMQTTException, adafruit_espatcontrol.OKError,
                    AdafruitIO_MQTTError) as ex:
                if self.debug:
                    print("EXCEPTION: Failed to get data in loop()", repr(ex))
                try_reconnect = True

        return dw_poll_ok

    def publish(self, p_data, p_fields):
        ok = [False] * len(p_fields)
        try_reconnect = False
        if self.debug:
            print("publish()")

        for idx, field_name in enumerate(p_fields):
            try:
                pub_name = self.pub_name[field_name]
            except KeyError:
                pub_name = self.update_pub_name(field_name)
            for _ in range(2):
                try:
                    if try_reconnect:
                        self.reset_and_reconnect()
                        try_reconnect = False
                    self.io.publish(pub_name, p_data[field_name])
                    ok[idx] = True
                    break
                except (ValueError, RuntimeError, AttributeError,
                        MQTT.MMQTTException, adafruit_espatcontrol.OKError,
                        AdafruitIO_MQTTError) as ex:
                    if self.debug:
                        print("EXCEPTION: Failed to publish()", repr(ex))
                    try_reconnect = True
                    time.sleep(RECONNECT_SLEEP)

        return all(ok)