albaintor / homeassistant_electrolux_status

Get the status from your Electrolux Care devices
MIT License
81 stars 20 forks source link

boolean / switch #24

Closed sciurius closed 6 months ago

sciurius commented 6 months ago

I did some investigations on the cavity light.

Cavity light is reported as:

  "cavityLight": {
    "access": "readwrite",
    "type": "boolean",
    "values": {
      "OFF": {},
      "ON": {}
    }
  },

The code in api.py reads:

        if values and access == "readwrite" and isinstance(values, dict) and len(values) > 0:
            if type != "number" or capability_def.get("min", None) is None:
                return SELECT

This means that the cavityLight is considered a SELECT since it has 2 values.

Cavity light is the only boolean in my capabilities so I do not have a reference whether this is normal.

Messages from the appliance are in the form {"cavityLight": true} and {"cavityLight": false} but to flip the switch, you must send {"cavityLight": "ON"} and {"cavityLight": "OFF"}.

So apparently a readwrite boolean is a special kind of SELECT.

albaintor commented 6 months ago

Hi, this is a bug on their implementation : it is advertised as a boolean but in takes strings. Other booleans are not defined like that and accept real booleans such as :

"autoDosing/adLocalFineTuning": {
    "access": "readwrite",
    "type": "boolean"
  },
sciurius commented 6 months ago

If other boolean attributes do not have strings, this is indeed a strange quirk in their software.

What we can do is:

Complication is that we do not know which string corresponds to true and false. Can we rely on the order of the values in the json?

Maybe it is better to specialcase this one...

albaintor commented 6 months ago

After checking the code, this field should be treated like a select entity with a drop down ON/OFF values, so it should work. Is it ?

sciurius commented 6 months ago

More or less... The oven reports values true and false, which get added to the dropdown list as (strings!) True and False. So there is a dropdown list of 4 values: True, False, ON and OFF. Only ON and OFF are accepted as input values by the server.

But having a dropdown list for a switch is slightly counter-intuitive.

The attached patch, minimal but admittingly slightly quick&dirty, fixes it for me. cavity.patch.zip

sciurius commented 6 months ago

It's getting better with git 58e06b5f3022415f2100030b5a12708d89d51aa5.

I'm not sure what the purpose is of is_on in switch.py as the values as reported by the oven are always boolean. I've added logging to is_on (see at end) and this is what I see:

Electrolux appliance state updated {"944188802_00:31177043-443E073615C0": {"cavityLight": true}}
Manually updated electrolux_status data
Electrolux is_on got value True
Electrolux is_on -> True
Electrolux appliance state updated {"944188802_00:31177043-443E073615C0": {"cavityLight": false}}
Manually updated electrolux_status data
Electrolux is_on got value None
Electrolux is_on -> True (cached)

The latter is not correct. Same happens when there is another update:

Electrolux appliance state updated {"944188802_00:31177043-443E073615C0": {"displayTemperatureC": 21.0, "displayTemperatureF": 69.8}}
Manually updated electrolux_status data
Electrolux is_on got value None
Electrolux is_on -> True (cached)

Checking for a string in switch is pointless, it will allways be boolean. The only condition when ON/OFF are needed is when the switch has values.

code:

    @property
    def is_on(self):
        """Return true if the binary_sensor is on."""
        value = self.extract_value()

        # Electrolux bug
        if value is not None and isinstance(value, str):
            _LOGGER.debug("Electrolux is_on got value \"%s\"", value)
            if value == "ON":
                value = True
            else:
                value = False
        else:
            _LOGGER.debug("Electrolux is_on got value %s", value)

        if value is None:
            _LOGGER.debug("Electrolux is_on -> %s (cached)", self._cached_value)
            return self._cached_value
        else:
            self._cached_value = value
        if value is not None and isinstance(value, str):
            _LOGGER.debug("Electrolux is_on -> \"%s\"", value)
        else:
            _LOGGER.debug("Electrolux is_on -> %s", value)
        return value

    async def switch(self, value: bool):
        client: OneAppApi = self.api

        # Electrolux bug
        if "values" in self.capability:
            if value:
                value = "ON"
            else:
                value = "OFF"

        if self.entity_source:
            command = {self.entity_source: {self.entity_attr: value}}
        else:
            command = {self.entity_attr: value}
        _LOGGER.debug("Electrolux set value %s", value)
        result = await client.execute_appliance_command(self.pnc_id, command)
        _LOGGER.debug("Electrolux set value result %s", result)
albaintor commented 6 months ago

Hi, so cavityLight is really a boolean ? I have applied this dirty code to get over with the capability which reported "ON" / "OFF" values If this is really a boolean I don't have to mess up with this then and I can remove this dirty code and make sure that a boolean is always treated as a boolean

sciurius commented 6 months ago

Unfortunately, not...

The cavity light advertises itself in the capabilities as a readwrite boolean, but with values as if it were an option list. The tweak in api.py (get_entity_type) works around this.

The cavity light reports its values as boolean, so that's ok.

However, when it comes to changing the value, it doesn't take a boolean, but here the strings ON resp. OFF must be used instead. That is what my proposed tweak in for switch.py (function switch) does. Function is_on should not need tweaking.