Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data pulish to MQTT Broker #3

Open
appi1 opened this issue May 17, 2024 · 7 comments
Open

Data pulish to MQTT Broker #3

appi1 opened this issue May 17, 2024 · 7 comments

Comments

@appi1
Copy link

appi1 commented May 17, 2024

Hi
great code, works great for my Outwell Arctic Cooler.
Do you have a hint for me how to forward the received data from the script via publish to am MQTT Broker?
thanks in advance and kind regards
Remo

@klightspeed
Copy link
Owner

I would probably use something like paho-mqtt to publish the data to the MQTT broker.

e.g.:

import paho.mqtt.client as mqtt
from fridge import Fridge, FridgeData
from typing import Optional, Union, Callable, Any


def publish_offline(mqttc: mqtt.Client, addr: str):
    mqttc.publish(f"fridge/{addr}/online", false)


def publish_status(mqttc: mqtt.Client, addr: str, current_data: FridgeData, previous_data: Optional[FridgeData]):
    if previous_data is None:
        mqttc.publish(f"fridge/{addr}/online", true)

    if previous_data is None or current_data != previous_data:
        info = {
            'on': data.powered_on,
            'runMode': data.run_mode.name,
            'lowVoltageLevel': data.battery_saver.name,
            'batteryVoltage': data.battery_voltage,
            'batteryChargePercent': data.battery_charge_percent,
            'temperatureUnit': data.temperature_unit.name,
            'units': {
            }
        }

        if data.unit1 is not None:
            info['units']['1'] = {
                'temperature': data.unit1.current_temperature,
                'target': data.unit1.target_temperature
            }
        
        if data.unit2 is not None:
            info['units']['2'] = {
                'temperature': data.unit2.current_temperature,
                'target': data.unit2.target_temperature
            }

        mqttc.publish(f'fridge/{addr}/state', info)


async def run(addr: str, bind: bool, poll: bool, pollinterval: int, mqttc: mqtt.Client):
    async with Fridge(addr) as fridge:
        if bind:
            await asyncio.wait_for(fridge.bind(), 30)

        try:
            query_response = await asyncio.wait_for(fridge.query(), 5)
        except TimeoutError:
            pass
        else:
            publish_status(mqttc, addr, query_response, None)
            last_status = query_response

        while poll:
            await asyncio.sleep(pollinterval)

            try:
                await asyncio.wait_for(fridge.query(), 5)
            except TimeoutError:
                if last_status is not None:
                    publish_offline(mqttc, addr)
                last_status = None
            else:
                publish_status(mqttc, addr, query_response, last_status)
                last_status = query_response


def main():
    parser = argparse.ArgumentParser(
        prog='fridge-mqtt.py',
        description='Fridge monitor for Alpicool / Brass Monkey fridges'
    )

    parser.add_argument('address', help='Bluetooth address of fridge')
    parser.add_argument('-b', '--bind', action='store_true', help='Press settings button on fridge to confirm fridge selection')
    parser.add_argument('-l', '--loop', action='store_true', help='Poll at regular intervals (default: query once)')
    parser.add_argument('-t', '--pollinterval', type = int, default=10, help='Poll interval in seconds (default: 10)')

    args = parser.parse_args()

    logging.basicConfig()

    mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)

    mqttc.connect("your.mqtt.server", 1883, 60)

    mqttc.loop_start()

    try:
        asyncio.run(run(args.address, args.bind, args.loop, args.pollinterval, mqttc))
    except KeyboardInterrupt:
        sys.stderr.write('Exiting\n')
        return
    finally:
        mqttc.publish(f'fridge/{args.address}/online', false)
        mqttc.loop_stop()


if __name__ == '__main__':
    main()
    

@appi1
Copy link
Author

appi1 commented May 19, 2024

thanks, it works partly...
i got the following Error:

root@raspberrypi:~# ./fridge-mqtt.py  ff:22:11:01:12:0a
ERROR:root:A message handler raised an exception: 'Fridge' object has no attribute 'on_query_response'.
Traceback (most recent call last):
  File "src/dbus_fast/message_bus.py", line 811, in dbus_fast.message_bus.BaseMessageBus._process_message
  File "/usr/lib/python3/dist-packages/bleak/backends/bluezdbus/manager.py", line 874, in _parse_msg
    on_value_changed(message.path, self_interface["Value"])
  File "/usr/lib/python3/dist-packages/bleak/backends/bluezdbus/client.py", line 172, in on_value_changed
    callback(bytearray(value))
  File "/root/fridge.py", line 302, in _notify_callback
    self.notify_query(decode_fridge_data(data[1:]))
  File "/root/fridge.py", line 317, in notify_query
    if self.on_query_response is not None:
       ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Fridge' object has no attribute 'on_query_response'

do you have a Idea?

@klightspeed
Copy link
Owner

I have committed a fix for that AttributeError - it seems merely declaring an attribute is not enough to define that attribute.

@appi1
Copy link
Author

appi1 commented May 19, 2024

very quick
bute the data ist not comming correct back from fridge.py:

root@raspberrypi:~# ./fridge-mqtt.py ff:22:11:01:12:0a
Traceback (most recent call last):
  File "/root/./fridge-mqtt.py", line 109, in <module>
    main()
  File "/root/./fridge-mqtt.py", line 99, in main
    asyncio.run(run(args.address, args.bind, args.loop, args.pollinterval, mqttc))
  File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/root/./fridge-mqtt.py", line 59, in run
    publish_status(mqttc, addr, query_response, None)
  File "/root/./fridge-mqtt.py", line 24, in publish_status
    'on': data.powered_on,
          ^^^^
NameError: name 'data' is not defined

@klightspeed
Copy link
Owner

Sorry - current_data should have been data.

Please try changing the publish_status function:

def publish_status(mqttc: mqtt.Client, addr: str, data: FridgeData, previous_data: Optional[FridgeData]):
    if previous_data is None:
        mqttc.publish(f"fridge/{addr}/online", true)

    if previous_data is None or data != previous_data:
        info = {
            'on': data.powered_on,
            'runMode': data.run_mode.name,
            'lowVoltageLevel': data.battery_saver.name,
            'batteryVoltage': data.battery_voltage,
            'batteryChargePercent': data.battery_charge_percent,
            'temperatureUnit': data.temperature_unit.name,
            'units': {
            }
        }

        if data.unit1 is not None:
            info['units']['1'] = {
                'temperature': data.unit1.current_temperature,
                'target': data.unit1.target_temperature
            }
        
        if data.unit2 is not None:
            info['units']['2'] = {
                'temperature': data.unit2.current_temperature,
                'target': data.unit2.target_temperature
            }

        mqttc.publish(f'fridge/{addr}/state', info)

@klightspeed
Copy link
Owner

I have now included a fridge_mqtt.py script in the repository.

@appi1
Copy link
Author

appi1 commented May 20, 2024

thank you for your support, but it is not so easy to bring it up to work....... my problem, to less python knowledge....

at the end i added a pulish line to the print function anf it works for me

def print_fridge_data(data: FridgeData):
'''Dump a JSON representation of the fridge data to standard output'''
print(json.dumps(data.to_dict()))
publish.single("Womo/fridge/sensor", json.dumps(data.to_dict()), hostname="192.168.8.1")``

as a next action i will try to switch the cooler on and off over BT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants