Skip to content
New issue

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

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

Already on GitHub? # to your account

Any one wrapped this as a Home Assistant Add-On? #174

Open
leaskovski opened this issue Sep 8, 2024 · 2 comments
Open

Any one wrapped this as a Home Assistant Add-On? #174

leaskovski opened this issue Sep 8, 2024 · 2 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed

Comments

@leaskovski
Copy link

Has any wrapped this up as a docker file that can be used with Home Assistant? I might have a go at trying to do this, but don't have much experience, however if someone has already done it, then great!!!

@TheRoarman
Copy link

or an add-on via HACS for some that are running HA OS

@AlmightyCZ
Copy link

Workaround: Using withings-sync with AppDaemon in Home Assistant

I put together this workaround to integrate withings-sync with Home Assistant using AppDaemon. It's the result of trial and error, so it might not be perfect. Here's how to set it up:


Steps:

  1. Install AppDaemon Add-On:

    • Install the AppDaemon add-on from the Home Assistant Add-on Store.
    • Enable "Start on boot" and start the add-on.
  2. Add Required Python Packages:

    • In the AppDaemon configuration, add these to the python_packages section:
      • withings-sync
      • pexpect
    • Save and restart AppDaemon.
  3. Create the Script:

    • In your AppDaemon apps folder (e.g., data/addon_configs/a0d7b954_appdaemon/apps/), create a file withings_sync_runner.py with this code:
import appdaemon.plugins.hass.hassapi as hass
import subprocess
import time
import pexpect
import os

def read_and_clear_file(filepath):
    """Reads and clears the content of the file, returns the content."""
    with open(filepath, 'r', encoding='utf-8') as file:
        content = file.read().strip()
    if content:
        # Clear the file content after reading
        with open(filepath, 'w', encoding='utf-8') as file:
            file.write('')
    return content


class WithingsSyncRunner(hass.Hass):

    def initialize(self):
        self.listen_event(self.run_sync_event, "WITHINGS_SYNC_START")
        self.listen_event(self.run_test, "WITHINGS_SYNC_TEST")
    
    def ha_notify(self, msg):
        self.call_service("notify/pavel_notification_group", title="Withings Sync", message=msg)

    def run_test(self, event_name, data, kwargs):
        self.ha_notify("test")

    def log_error(self, message):
        """Logs error messages using `ascii_encode=False`."""
        self.log(message, ascii_encode=False)
        self.ha_notify(message)

    def run_sync_event(self, event_name, data, kwargs):
        os.environ["WITHINGS_USER"] = "/config/apps/withings_user.json"
        os.environ["GARMIN_SESSION"] = "/config/apps/garmin_session"
        self.log("Starting sync...")
        child = pexpect.spawn('withings-sync --garmin-username=USERNAME --garmin-password=PASSWORD')

        prompts = ['Enter MFA code', 'Token :', 'MFA code:']

        try:
            while True:
                index = child.expect(prompts + [pexpect.EOF, '\n'], timeout=-1)
                if index < len(prompts):  # Prompt detected
                    token_msg = f"Prompt detected: {prompts[index]}"
                    self.ha_notify(token_msg)
                    self.log(token_msg, ascii_encode=False)
                    token = None
                    waiting_time = 0
                    max_waiting_time = 300  # 5 minutes
                    self.log("Waiting for code...", ascii_encode=False)
                    while not token and waiting_time < max_waiting_time:
                        token = read_and_clear_file('/config/apps/input.txt')
                        time.sleep(5)  # Waiting for the file to be updated
                        waiting_time += 5
                    if not token:
                        self.log_error("Time to enter the code has expired.")
                        break
                    self.log(f"Code received: {token}", ascii_encode=False)
                    child.sendline(token)
                elif index == len(prompts):  # EOF - End of file (process finished)
                    self.log("Process finished", ascii_encode=False)
                    break
                else:
                    # Output the response if no specific prompt is detected
                    print(child.before.decode('utf-8', errors='ignore').strip())
        except pexpect.exceptions.TIMEOUT:
            self.log_error("Timeout occurred.")
            self.log(f"Output before timeout: {child.before.decode('utf-8', errors='ignore').strip()}", ascii_encode=False)
        except pexpect.exceptions.EOF:
            self.log_error("Process ended prematurely.")
            self.log(f"Output before EOF: {child.before.decode('utf-8', errors='ignore').strip()}", ascii_encode=False)
        except Exception as e:
            self.log_error(f"Unexpected error: {e}")
  • Replace USERNAME and PASSWORD with your Garmin credentials.
  • Replace notify/pavel_notification_group with your device/group for MFA and error notifications.
  1. Trigger Sync:
    • Use a Home Assistant automation or script to fire the WITHINGS_SYNC_START event.

      Example for automation actions:

         actions:
           - event: WITHINGS_SYNC_START
             event_data: {}

Notes:

  • MFA Handling: When prompted, enter the MFA code into input.txt (you may need to create the file first). The script will pick it up automatically.
  • Security: Credentials are stored in plain text, so use with caution.

(Disclaimer: I've never worked with Python before, so this is just a workaround I managed to put together. Any suggestions or improvements are more than welcome!)

@longstone longstone added documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed labels Dec 3, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants