galletn / iaqualink

Home Assistant Iaqualink Vacuums Robots
9 stars 2 forks source link

Stop Start functionality #1

Closed galletn closed 3 months ago

galletn commented 1 year ago

Need to investigate stop start functionality

ppastur commented 1 year ago

@galletn , I setup mitmproxy and managed to capture turning the robot on and off. It looks like its all websocket traffic which means Im a little out of my depth. I am however happy to work with you to provide you with the capture and also to test if that is helpful?

Here is where the coms changed to websocket

GET /devices HTTP/1.1
Host: prod-socket.zodiac-io.com
Sec-WebSocket-Key:
Sec-WebSocket-Version: 13
Upgrade: websocket
Origin: https://prod-socket.zodiac-io.com
Authorization: <Authorization>
Connection: Upgrade
content-length: 0

HTTP/1.1 101 Switching Protocols
Date: Mon, 22 May 2023 10:51:20 GMT
Connection: upgrade
upgrade: websocket
sec-websocket-accept: 
content-length: 0

Here is the redacted websocket messages. Ive replaced userID, robot serial number and other serial/part numbers

SEND-->

{
    "action": "subscribe",
    "namespace": "authorization",
    "payload": {
        "userId": $USERID$
    },
    "service": "Authorization",
    "target": "$ROBOTSERIALNUMBER$",
    "version": 1
}

RECIEVE <--

{
    "namespace": "authorization",
    "payload": {
        "data": [
            {
                "accelero": [
                    -85,
                    -31,
                    1049
                ],
                "angleRotation": 183,
                "cleanerPos": 1,
                "cumulAngleRotation": 502,
                "cycleId": 1684646687,
                "floorBlockageCnt": 0,
                "gyro": [
                    1,
                    -2,
                    1
                ],
                "iPump": 2442,
                "iTract1": 99,
                "iTract2": 109,
                "lastMoveLength": 13,
                "loopCnt": 200,
                "magneto": [
                    0,
                    0,
                    0
                ],
                "movementId": 1,
                "patternId": 0,
                "pwmPump": 100,
                "pwmTract1": 80,
                "pwmTract2": 80,
                "stairsCnt": 1764,
                "tiltCnt": 32692,
                "timestamp": 1684650932,
                "totalHours": 348,
                "vEbox": 28,
                "vRobot": 28,
                "vSensor": 11,
                "wallCnt": 1660
            }
        ],
        "ota": {
            "status": "UP_TO_DATE"
        },
        "robot": {
            "metadata": {
                "desired": {
                    "equipment": {
                        "robot": {}
                    }
                },
                "reported": {
                    "aws": {
                        "session_id": {
                            "timestamp": 1684751732
                        },
                        "status": {
                            "timestamp": 1684751732
                        },
                        "timestamp": {
                            "timestamp": 1684751732
                        }
                    },
                    "dt": {
                        "timestamp": 1681926072
                    },
                    "eboxData": {
                        "completeCleanerPn": {
                            "timestamp": 1681926072
                        },
                        "completeCleanerSn": {
                            "timestamp": 1681926072
                        },
                        "controlBoxPn": {
                            "timestamp": 1681926072
                        },
                        "controlBoxSn": {
                            "timestamp": 1681926072
                        },
                        "motorBlockSn": {
                            "timestamp": 1681926072
                        },
                        "powerSupplySn": {
                            "timestamp": 1681926072
                        },
                        "sensorBlockSn": {
                            "timestamp": 1681926072
                        }
                    },
                    "equipment": {
                        "robot": {
                            "canister": {
                                "timestamp": 1684571511
                            },
                            "customCyc": {
                                "timestamp": 1681926072
                            },
                            "customIntensity": {
                                "timestamp": 1681926072
                            },
                            "cycleStartTime": {
                                "timestamp": 1684752278
                            },
                            "durations": {
                                "customTim": {
                                    "timestamp": 1681926072
                                },
                                "deepTim": {
                                    "timestamp": 1681926072
                                },
                                "firstSmartTim": {
                                    "timestamp": 1681926072
                                },
                                "quickTim": {
                                    "timestamp": 1681926072
                                },
                                "smartTim": {
                                    "timestamp": 1681926072
                                },
                                "waterTim": {
                                    "timestamp": 1681926072
                                }
                            },
                            "equipmentId": {
                                "timestamp": 1681926073
                            },
                            "errorCode": {
                                "timestamp": 1681926072
                            },
                            "errorState": {
                                "timestamp": 1681926072
                            },
                            "firstSmrtFlag": {
                                "timestamp": 1681926072
                            },
                            "liftControl": {
                                "timestamp": 1681926072
                            },
                            "logger": {
                                "timestamp": 1681926072
                            },
                            "prCyc": {
                                "timestamp": 1684752605
                            },
                            "repeat": {
                                "timestamp": 1681926072
                            },
                            "rmt_ctrl": {
                                "timestamp": 1681926072
                            },
                            "scanTimeDuration": {
                                "timestamp": 1681926072
                            },
                            "schConf0Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf0Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf0Min": {
                                "timestamp": 1681926072
                            },
                            "schConf0Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf0WDay": {
                                "timestamp": 1681926072
                            },
                            "schConf1Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf1Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf1Min": {
                                "timestamp": 1681926072
                            },
                            "schConf1Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf1WDay": {
                                "timestamp": 1681926072
                            },
                            "schConf2Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf2Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf2Min": {
                                "timestamp": 1681926072
                            },
                            "schConf2Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf2WDay": {
                                "timestamp": 1681926072
                            },
                            "schConf3Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf3Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf3Min": {
                                "timestamp": 1681926072
                            },
                            "schConf3Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf3WDay": {
                                "timestamp": 1681926072
                            },
                            "schConf4Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf4Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf4Min": {
                                "timestamp": 1681926072
                            },
                            "schConf4Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf4WDay": {
                                "timestamp": 1681926072
                            },
                            "schConf5Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf5Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf5Min": {
                                "timestamp": 1681926072
                            },
                            "schConf5Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf5WDay": {
                                "timestamp": 1681926072
                            },
                            "schConf6Enable": {
                                "timestamp": 1681926072
                            },
                            "schConf6Hour": {
                                "timestamp": 1681926072
                            },
                            "schConf6Min": {
                                "timestamp": 1681926072
                            },
                            "schConf6Prt": {
                                "timestamp": 1681926072
                            },
                            "schConf6WDay": {
                                "timestamp": 1681926072
                            },
                            "sensors": {
                                "sns_1": {
                                    "state": {
                                        "timestamp": 1684752306
                                    },
                                    "type": {
                                        "timestamp": 1681926072
                                    }
                                },
                                "sns_2": {
                                    "state": {
                                        "timestamp": 1684752306
                                    },
                                    "type": {
                                        "timestamp": 1681926072
                                    }
                                },
                                "sns_3": {
                                    "state": {
                                        "timestamp": 1681926072
                                    },
                                    "type": {
                                        "timestamp": 1681926072
                                    }
                                }
                            },
                            "state": {
                                "timestamp": 1684752605
                            },
                            "stepper": {
                                "timestamp": 1681926072
                            },
                            "stepperAdjTime": {
                                "timestamp": 1681926072
                            },
                            "totalHours": {
                                "timestamp": 1684752280
                            },
                            "vr": {
                                "timestamp": 1681926073
                            }
                        }
                    },
                    "jobId": {
                        "timestamp": 1651189088
                    },
                    "sn": {
                        "timestamp": 1681926072
                    },
                    "vr": {
                        "timestamp": 1681926072
                    }
                }
            },
            "state": {
                "desired": {
                    "equipment": {
                        "robot": {}
                    }
                },
                "reported": {
                    "aws": {
                        "session_id": "7d0137d2-06bd-45f4-8be9-e3f503886d65",
                        "status": "connected",
                        "timestamp": 1684751732835
                    },
                    "dt": "vr",
                    "eboxData": {
                        "completeCleanerPn": "${some-part-number}",
                        "completeCleanerSn": "${some-serial-number}",
                        "controlBoxPn": "${some-part-number}",
                        "controlBoxSn": "${some-serial-number}",
                        "motorBlockSn": "${some-serial-number}",
                        "powerSupplySn": "${some-serial-number}",
                        "sensorBlockSn": ""
                    },
                    "equipment": {
                        "robot": {
                            "canister": 0,
                            "customCyc": 1,
                            "customIntensity": 0,
                            "cycleStartTime": 1684752276,
                            "durations": {
                                "customTim": 150,
                                "deepTim": 165,
                                "firstSmartTim": 150,
                                "quickTim": 75,
                                "smartTim": 111,
                                "waterTim": 45
                            },
                            "equipmentId": "JY21",
                            "errorCode": 0,
                            "errorState": 0,
                            "firstSmrtFlag": 0,
                            "liftControl": 0,
                            "logger": 0,
                            "prCyc": 1,
                            "repeat": 0,
                            "rmt_ctrl": 0,
                            "scanTimeDuration": 30,
                            "schConf0Enable": 0,
                            "schConf0Hour": 0,
                            "schConf0Min": 0,
                            "schConf0Prt": 1,
                            "schConf0WDay": 0,
                            "schConf1Enable": 0,
                            "schConf1Hour": 0,
                            "schConf1Min": 0,
                            "schConf1Prt": 1,
                            "schConf1WDay": 1,
                            "schConf2Enable": 0,
                            "schConf2Hour": 0,
                            "schConf2Min": 0,
                            "schConf2Prt": 1,
                            "schConf2WDay": 2,
                            "schConf3Enable": 0,
                            "schConf3Hour": 0,
                            "schConf3Min": 0,
                            "schConf3Prt": 1,
                            "schConf3WDay": 3,
                            "schConf4Enable": 0,
                            "schConf4Hour": 0,
                            "schConf4Min": 0,
                            "schConf4Prt": 1,
                            "schConf4WDay": 4,
                            "schConf5Enable": 0,
                            "schConf5Hour": 0,
                            "schConf5Min": 0,
                            "schConf5Prt": 1,
                            "schConf5WDay": 5,
                            "schConf6Enable": 0,
                            "schConf6Hour": 0,
                            "schConf6Min": 0,
                            "schConf6Prt": 1,
                            "schConf6WDay": 6,
                            "sensors": {
                                "sns_1": {
                                    "state": 0,
                                    "type": "temperature"
                                },
                                "sns_2": {
                                    "state": 0,
                                    "type": "pressure"
                                },
                                "sns_3": {
                                    "state": 0,
                                    "type": "compass"
                                }
                            },
                            "state": 0,
                            "stepper": 0,
                            "stepperAdjTime": 15,
                            "totalHours": 349,
                            "vr": "V32E47"
                        }
                    },
                    "jobId": "Job_VR_V32E47_V32C47",
                    "sn": "$ROBOTSERIALNUMBER$",
                    "vr": "V32C47"
                }
            },
            "timestamp": 1684752680,
            "version": 138294
        }
    },
    "service": "Authorization",
    "target": "$ROBOTSERIALNUMBER$"
}

SEND --> (start clean cycle on the app)

{
    "action": "setCleanerState",
    "namespace": "vr",
    "payload": {
        "clientToken": "$USERID$|",
        "state": {
            "desired": {
                "equipment": {
                    "robot": {
                        "state": 1
                    }
                }
            }
        }
    },
    "service": "StateController",
    "target": "$ROBOTSERIALNUMBER$",
    "version": 1
}

notice state:1

RECIEVE <-- (a bunch of these)

{
    "event": "StateReported",
    "payload": {
        "clientToken": "$USERID$|",
        "metadata": {
            "desired": {
                "equipment": {
                    "robot": {
                        "state": {
                            "timestamp": 1684752688
                        }
                    }
                }
            }
        },
        "state": {
            "desired": {
                "equipment": {
                    "robot": {
                        "state": 1
                    }
                }
            }
        },
        "timestamp": 1684752688,
        "version": 138295
    },
    "service": "StateStreamer",
    "target": "$ROBOTSERIALNUMBER$",
    "version": 1
}

SEND --> (stop clean cycle on the app)

{
    "action": "setCleanerState",
    "namespace": "vr",
    "payload": {
        "clientToken": "$USERID$|",
        "state": {
            "desired": {
                "equipment": {
                    "robot": {
                        "state": 0
                    }
                }
            }
        }
    },
    "service": "StateController",
    "target": "$ROBOTSERIALNUMBER$",
    "version": 1
}

notice state: 0

RECIEVE <-- (a bunch of these)

{
    "event": "StateReported",
    "payload": {
        "clientToken": "$USERID$|",
        "metadata": {
            "desired": {
                "equipment": {
                    "robot": {
                        "state": {
                            "timestamp": 1684752705
                        }
                    }
                }
            }
        },
        "state": {
            "desired": {
                "equipment": {
                    "robot": {
                        "state": 0
                    }
                }
            }
        },
        "timestamp": 1684752705,
        "version": 138300
    },
    "service": "StateStreamer",
    "target": "$ROBOTSERIALNUMBER$",
    "version": 1
}

Let me know if you need more as I have everything set up and ready to capture more traffic.

galletn commented 1 year ago

removed a few authorization and key hashes to make sure no one gets any password of yours đź‘Ť

looks like this is what we need to try:

https://websocket-client.readthedocs.io/en/latest/examples.html

I'll see when I can test it out.

ppastur commented 1 year ago

moved some posts to new issues to deal with them separately to start/stop functionality.

ppastur commented 1 year ago

I found some GO code here for the iAqualink websocket connection and tried to convert. Is this helpful;

import json
import time
import random
import websocket
import contextlib
import logging
from websocket._abnf import ABNF

APIKey = "EOOEMOW4YR6QNB07"
IAquaLinkAPIBase = "https://r-api.iaqualink.net"
WebSocketAPIBase = "https://prod-socket.zodiac-io.com"
ZodiacAPIBase = "https://prod.zodiac-io.com"

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

class DeviceWebSocketInputSubscribePayload:
    def __init__(self, UserID):
        self.UserID = UserID

class DeviceWebSocketInputSetStatePayload:
    def __init__(self, state, ClientToken):
        self.State = state
        self.ClientToken = ClientToken

class DeviceWebSocketInput:
    def __init__(self, Version, Action, Namespace, Service, Target, Payload):
        self.Version = Version
        self.Action = Action
        self.Namespace = Namespace
        self.Service = Service
        self.Target = Target
        self.Payload = Payload

class DeviceWebSocketOutputSubscribePayload:
    def __init__(self, Robot):
        self.Robot = Robot

class DeviceWebSocketOutput:
    def __init__(self, Event, Service, Target, Payload):
        self.Event = Event
        self.Service = Service
        self.Target = Target
        self.Payload = Payload

class Client:
    def __init__(self):
        self.IAquaLinkAPIBase = IAquaLinkAPIBase
        self.WebSocketAPIBase = WebSocketAPIBase
        self.ZodiacAPIBase = ZodiacAPIBase
        self.APIKey = APIKey
        self.AuthenticationToken = None
        self.IDToken = None
        self.UserID = None
        self.websocket = None

    def login(self, username, password):
        # Same as the login method in the Go code

    def device_web_socket(self, deviceID, actions):
        self.init()

        url = self.WebSocketAPIBase.replace("https://", "wss://") + "/devices"
        header = ["Authorization: " + self.IDToken]

        logger.debug("Connecting to Web Socket: %s", url)
        self.websocket = websocket.WebSocket()
        self.websocket.connect(url, header=header)

        userId = int(self.UserID)
        logger.debug("User ID: %d", userId)

        output = {}

        subscribeInput = DeviceWebSocketInput(
            Version=1,
            Action="subscribe",
            Namespace="authorization",
            Service="Authorization",
            Target=deviceID,
            Payload=DeviceWebSocketInputSubscribePayload(userId)
        )

        def json_to_string(value):
            contents = json.dumps(subscribeInput.__dict__)
            return contents

        logger.debug("Writing: %s", json_to_string(subscribeInput))
        self.websocket.send(json_to_string(subscribeInput))

        with contextlib.suppress(Exception):
            while True:
                contents = self.websocket.recv()
                logger.debug("Received: %s", contents)
                if len(contents) == 0:
                    time.sleep(0.1)
                    continue

                subscribeOutput = json.loads(contents)
                if subscribeOutput["Service"] != subscribeInput.Service:
                    logger.debug("Service does not match: got %s (expected %s)", subscribeOutput["Service"], subscribeInput.Service)
                    continue
                if subscribeOutput["Namespace"] != subscribeInput.Namespace:
                    logger.debug("Namespace does not match: got %s (expected %s)", subscribeOutput["Namespace"], subscribeInput.Namespace)
                    continue
                if subscribeOutput["Target"] != subscribeInput.Target:
                    logger.debug("Target does not match: got %s (expected %s)", subscribeOutput["Target"], subscribeInput.Target)
                    continue

                subscribeOutputPayload = DeviceWebSocketOutputSubscribePayload(
                    Robot=subscribeOutput["Payload"]["Robot"]
                )

                output["_equipment_length"] = str(len(subscribeOutputPayload.Robot["State"]["Reported"]["Equipment"]))
                for key, value in subscribeOutputPayload.Robot["State"]["Reported"]["Equipment"].items():
                    output["_robot_key"] = key
                    output["mode"] = str(value["Mode"])
                    break

                break

        def generate_thing():
            letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
            b = ''.join(random.choice(letters) for _ in range(22))
            return b

        for action in actions:
            logger.debug("Performing action: %s", action)
            if action == "start" or action == "stop":
                mode = 1 if action == "start" else 0

                thing1 = generate_thing()
                thing2 = generate_thing()

                subscribeInput = DeviceWebSocketInput(
                    Version=1,
                    Action="setState",
                    Namespace="cyclonext",
                    Service="StateController",
                    Target=deviceID,
                    Payload=DeviceWebSocketInputSetStatePayload(
                        state={
                            "desired": {
                                "equipment": {
                                    output["_robot_key"]: {
                                        "mode": mode
                                    }
                                }
                            }
                        },
                        ClientToken=f"{userId}|{thing1}|{thing2}"
                    )
                )

                logger.debug("Writing: %s", json_to_string(subscribeInput))
                self.websocket.send(json_to_string(subscribeInput))

                with contextlib.suppress(Exception):
                    while True:
                        contents = self.websocket.recv()
                        logger.debug("Received: %s", contents)
                        if len(contents) == 0:
                            time.sleep(0.1)
                            continue

                        subscribeOutput = json.loads(contents)
                        if subscribeOutput["Event"] != "StateReported":
                            logger.debug("Event does not match: got %s (expected %s)", subscribeOutput["Event"], "StateReported")
                            continue
                        if subscribeOutput["Service"] != "StateStreamer":
                            logger.debug("Service does not match: got %s (expected %s)", subscribeOutput["Service"], "StateStreamer")
                            continue
                        if subscribeOutput["Target"] != subscribeInput.Target:
                            logger.debug("Target does not match: got %s (expected %s)", subscribeOutput["Target"], subscribeInput.Target)
                            continue

                        output["mode"] = str(mode)
                        break

        return output
galletn commented 1 year ago

this might take a while to implement, I'll see what I can do, seems to contain some interesting parts for sure!

ppastur commented 1 year ago

this might take a while to implement, I'll see what I can do, seems to contain some interesting parts for sure!

Please let me know if I can help you. Also please send me your postman collection so I can test/play with the execute command bit - It will be interesting to see if my robot goes to offline like yours.

miannelli516 commented 1 year ago

Hello I have a Polaris - VRX iQ+ robot. Will this integration into Home Assistant work with this robot? Also, I noticed when I create the sensor in home assistant it's asking for an API key. Where can I get that from? Any assistance anyone can provide is greatly appreciated. Also, I would be more than happy to help with anything on this project.

ppastur commented 1 year ago

Hello I have a Polaris - VRX iQ+ robot. Will this integration into Home Assistant work with this robot? Also, I noticed when I create the sensor in home assistant it's asking for an API key. Where can I get that from? Any assistance anyone can provide is greatly appreciated. Also, I would be more than happy to help with anything on this project.

It shouldn't be asking you for an API key as its built into the code. It is 'EOOEMOW4YR6QNB07'. As to whether your device is supported - the short answer is not sure. I'd encourage you to install via HACS, set-up your sensor in config.yaml and see if you get data populated in the sensor. If it does, great. if not then check your logs and report back here by opening a new issue.

miannelli516 commented 1 year ago

Awesome thank you so much!! I just tried it and everything is working! Just curious, is the start/stop feature still being worked on?

ppastur commented 1 year ago

It is. @galletn may I suggest updating the readme to include confirmed support for Polaris - VRX iQ+

galletn commented 1 year ago

added the new confirmed supported robot, and indeed, I want to make the start stop work indeed, but might need some time as its quite busy @work lately.

miannelli516 commented 1 year ago

Ok no problem. Let me know if I can help at all with anything

LOki61 commented 1 year ago

added the new confirmed supported robot, and indeed, I want to make the start stop work indeed, but might need some time as its quite busy @work lately.

Also, Polaris alpha IQ works with the integration.

ppastur commented 1 year ago

Hi @galletn just checking in with you. How are you progressing? Can I offer any help? You're probably very busy at work - no worries if that's the case.

galletn commented 1 year ago

hi @ppastur indeed, busy at work, also weekends, so no progress yet. I'll try to see when I can free up some time to get this websocket thing going.

galletn commented 1 year ago

@ppastur I'm fixing some parts of the code first, but I'm almost going to do this stop start too. Read some stuff on it and I setup a development environment of HA so that I don't break and need to reboot my HA all the time :)

PS for latest version you will have some breaking changes, as I needed to move the folder to not interfere with the other integration of Iaqualink #6 so you need to remove it from your config first, update and add it back to the config and watch that the platform is now iaqualinkRobots instead of iaqualink

ppastur commented 1 year ago

@ppastur I'm fixing some parts of the code first, but I'm almost going to do this stop start too. Read some stuff on it and I setup a development environment of HA so that I don't break and need to reboot my HA all the time :)

PS for latest version you will have some breaking changes, as I needed to move the folder to not interfere with the other integration of Iaqualink #6 so you need to remove it from your config first, update and add it back to the config and watch that the platform is now iaqualinkRobots instead of iaqualink

Thanks for the heads up @galletn. I uninstalled, changed the platform sensor in the config and reinstalled. After some tweaks to the frontend referencing the old sensor, it all works as expected.

SanderM2 commented 1 year ago

Any news yet on the start/stop?

galletn commented 6 months ago

I revamped some of the code already, but I'm having struggles creating both a sensor and button ... I'll try to find some more time to invest, but unless I get that part fixed it will be very hard to implement this. Any help is welcome đź‘Ť

ppastur commented 6 months ago

What help do you need? I can dedicate some time.

galletn commented 6 months ago

Well creating one or more sensors works like a charm, but as soon as I try to create a button and a sensor from the same integration it's not creating both ... I'm not a Python expert so ... probably I'm doing something wrong in the constructors...

bellow my button.py file

could also be that we need to move the creation into the init.py ... but I did not find enough time to investigate that deep.

the help pages of HA are a bit lacking on that end, tried chat GPT too, but not with desired result yet.

from __future__ import annotations
from homeassistant.components.button import ButtonEntity

from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None
) -> None:
    """Set up the sensor platform."""
    add_entities([iaqualinkRobotStart(config)])

class iaqualinkRobotStart(ButtonEntity):
    _attr_has_entity_name = True
    _attr_name = "Start"

    @property
    def unique_id(self) -> str:
        """Return the name of the sensor."""
        return (
            f"Start"
        )

    @property
    def name(self):
        """Name of the entity."""
        return "Start"

    def __init__(self, config):
        # Apply throttling to methods using configured interval

    def press(self) -> None:
        """Handle the button press."""
galletn commented 3 months ago

Been a while but just a heads up, the code is ready for stop/start ... the only job I still need to do is get a damn button into HA 🗡️ once that is done ... we are up and running! Keep you posted!

galletn commented 3 months ago

so I found out how to do it with the buttons, but I did not like the setup. So I'm changing all the code towards Vacuum entity that provides stop start functionality as needed.

I'll release the code tomorrow, and hope this week to implement the stop start code that works into it. Also I got some help of one of the other guys here that has another device type so that cyclones should also be supported!

keep you posted!

beware that tomorrows code will break your stuff! But it will have many advantages in the end

image
ppastur commented 3 months ago

Thanks for the update - thats great news.

galletn commented 3 months ago

please try out the new vacuum entity and let me know how it works!

ppastur commented 3 months ago

Hi thats awesome. I assume this will or may break my current working card, sensors etc. Do you have a recommendation on how I can test and if required roll back?

galletn commented 3 months ago

HI @ppastur you can keep both for now, I implemented the vacuum entity, you can enter both sensor and vacuum in your yaml.

ppastur commented 3 months ago

@galletn I Tried the new version and I can confirm it works fine. I however cant for the life of me work out how to create a button in my YAML to Start/Stop cleaning. Can you share your yaml?

galletn commented 3 months ago

@ppastur you need to add as written in the readme:

vacuum:
  - platform: iaqualinkRobots
    username: <username>
    password: <password>
    api_key: EOOEMOW4YR6QNB07
    name: <Robot name, will also be the sensor name>

so you now have:

sensor:
  - platform: iaqualinkRobots
    username: <username>
    password: <password>
    api_key: EOOEMOW4YR6QNB07
    name: <Robot name, will also be the sensor name>

but you can put both ... but then add to your dashboard the vacuum sensor too it looks like this and has buttons when you open the entity:

image image
ppastur commented 3 months ago

@galletn Thanks - I have already done that and have the same as you. What I was asking about was how to add a button into a Lovelace card or entity card so you don't have to go to the vacuum entity to start / stop etc

galletn commented 3 months ago

@ppastur just add the vacuum entity.

image

or add it to an existing card.

once added if you click on it you see the start & stop button.

ppastur commented 3 months ago

Hi @galletn Thank you - I understand and have that already. image

What Im asking about is slightly different. I have a card that I created for my Pool area and robot. See here I have 2 cards, one using data from sensor.py and the other using the new vacuum.py

image

If I click on the entity I get the standard entity card and I can then start/stop the robot. What I would like is to create a button on the card (like the one shown) that allows me to start/stop activities without having to bring up a the entity card. Make sense? I've tried to use the action call service vacuum.start / vacuume.stop / vacuume.toggle but it is unreliable. I even created a script that call service vacuum.start / vacuume.stop / vacuume.toggleand and had the button call the script but again its unreliable but I suspect that is on me and how I'm doing it as the start / stop functionality works great - you have done a splendid job. I was asking to see if you had managed to get a button in a card to directly control the robot without having to go into the entity

LOki61 commented 3 months ago

I can’t recall, however, I think it’s in the hassio forums somewhere to be able to bring that on to its own card. I set up with my pool lights through scripts.. I just need to get home and have a look.

On Thu, Jul 4, 2024 at 8:19 PM ppastur @.***> wrote:

Hi @galletn https://github.com/galletn Thank you - I understand and have that already. image.png (view on web) https://github.com/galletn/iaqualink/assets/91765182/528a84aa-fc7d-4a2c-9b65-532ad7111945

What Im asking about is slightly different. I have a card that I created for my Pool area and robot. See here I have 2 cards, one using data from sensor.py and the other using the new vacuum.py

image.png (view on web) https://github.com/galletn/iaqualink/assets/91765182/9f94dc70-2e77-4122-8e7f-1345dce1194f

If I click on the entity I get the standard entity card and I can then start/stop the robot. What I would like is to create a button on the card (like the one shown) that allows me to start/stop activities without having to bring up a the entity card. Make sense? I've tried to use the action call service vacuum.start / vacuume.stop / vacuume.toggle but it is unreliable. I even created a script that call service vacuum.start / vacuume.stop / vacuume.toggleand and had the button call the script but again its unreliable but I suspect that is on me and how I'm doing it as the start / stop functionality works great - you have done a splendid job. I was asking to see if you had managed to get a button in a card to directly control the robot without having to go into the entity

— Reply to this email directly, view it on GitHub https://github.com/galletn/iaqualink/issues/1#issuecomment-2209661509, or unsubscribe https://github.com/notifications/unsubscribe-auth/AVQMRHJNAV6JXEEJ3ZOFDBLZKXRBLAVCNFSM6AAAAABKCKUMBCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMBZGY3DCNJQHE . You are receiving this because you commented.Message ID: @.***>

LOki61 commented 3 months ago
  1. Go to an UI dashboard
  2. Click on the three dots right top
  3. Choose „edit“
  4. Click „Add new card“
  5. Choose „button“
  6. In the button card config. dialog click „tap action“
  7. In the opened dropdown click „call service“
  8. In the second dropdown choose the service you want to call
  9. Fill in the form

On Thu, Jul 4, 2024 at 8:22 PM ian dcruZ @.***> wrote:

I can’t recall, however, I think it’s in the hassio forums somewhere to be able to bring that on to its own card. I set up with my pool lights through scripts.. I just need to get home and have a look.

On Thu, Jul 4, 2024 at 8:19 PM ppastur @.***> wrote:

Hi @galletn https://github.com/galletn Thank you - I understand and have that already. image.png (view on web) https://github.com/galletn/iaqualink/assets/91765182/528a84aa-fc7d-4a2c-9b65-532ad7111945

What Im asking about is slightly different. I have a card that I created for my Pool area and robot. See here I have 2 cards, one using data from sensor.py and the other using the new vacuum.py

image.png (view on web) https://github.com/galletn/iaqualink/assets/91765182/9f94dc70-2e77-4122-8e7f-1345dce1194f

If I click on the entity I get the standard entity card and I can then start/stop the robot. What I would like is to create a button on the card (like the one shown) that allows me to start/stop activities without having to bring up a the entity card. Make sense? I've tried to use the action call service vacuum.start / vacuume.stop / vacuume.toggle but it is unreliable. I even created a script that call service vacuum.start / vacuume.stop / vacuume.toggleand and had the button call the script but again its unreliable but I suspect that is on me and how I'm doing it as the start / stop functionality works great - you have done a splendid job. I was asking to see if you had managed to get a button in a card to directly control the robot without having to go into the entity

— Reply to this email directly, view it on GitHub https://github.com/galletn/iaqualink/issues/1#issuecomment-2209661509, or unsubscribe https://github.com/notifications/unsubscribe-auth/AVQMRHJNAV6JXEEJ3ZOFDBLZKXRBLAVCNFSM6AAAAABKCKUMBCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMBZGY3DCNJQHE . You are receiving this because you commented.Message ID: @.***>

ppastur commented 3 months ago

Yep - that's what I did previously. I think I found the problem. My script was set to single mode. I changed it to restart mode and it seems to be working now. Ill test a little more

ppastur commented 3 months ago

Ok - Confirmed and Tested. I managed to get a button in a card to work the start/stop

https://github.com/galletn/iaqualink/assets/91765182/ba2b1fd6-4ee4-48d2-b72b-3ed0fc097fe6

Check out the short vid

LOki61 commented 3 months ago

Can you share the script. Thanks

On Fri, Jul 5, 2024 at 7:28 AM ppastur @.***> wrote:

Ok - Confirmed and Tested. I managed to get a button in a card to work the start/stop

https://github.com/galletn/iaqualink/assets/91765182/ba2b1fd6-4ee4-48d2-b72b-3ed0fc097fe6

Check out the short vid

— Reply to this email directly, view it on GitHub https://github.com/galletn/iaqualink/issues/1#issuecomment-2210707824, or unsubscribe https://github.com/notifications/unsubscribe-auth/AVQMRHLSDEQGNIX4MID6TY3ZKZ7OHAVCNFSM6AAAAABKCKUMBCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMJQG4YDOOBSGQ . You are receiving this because you commented.Message ID: @.***>

ppastur commented 3 months ago

sure - here you go

` alias: Vacuum toggle script sequence:

galletn commented 3 months ago

Nice video, do you mind I share it on the intro page, the delay in HA is normal, its between 0 and 30 seconds depending on when you clicked the start as the status updates are delay by that time window

ppastur commented 3 months ago

no problem - least I can do for you

ppastur commented 3 months ago

I can edit the video and remove the title Test new vaccum.py if you want

ppastur commented 3 months ago

https://github.com/galletn/iaqualink/assets/91765182/75806d16-6350-4a8d-84d0-7ce2996ae8f6

Here it is without vacuum.py test title