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

Add initial NodOn SIN-4-FP-21 (Adeo SIN-4-FP-21_EQU) quirk #3364

Merged
merged 11 commits into from
Nov 26, 2024

Conversation

ikruglov
Copy link
Contributor

@ikruglov ikruglov commented Sep 15, 2024

Proposed change

This PR adds a quirk for SIN-4-FP-21 (PilotWire). The device reports itself as a SmartPlug but it has special manufacturer-specific cluster which allows setting the following modes:

        Off = 0x00
        Comfort = 0x01
        Eco = 0x02
        FrostProtection = 0x03
        ComfortMinus1 = 0x04
        ComfortMinus2 = 0x05

Additional information

Important

The HA entity shown below is not yet implemented with this version.
See this comment for more information: #3364 (comment)

The result look like this in Home Assitant:
Screenshot 2024-09-22 at 22 16 55

The PR also contains a solution for Adeo SIN-4-FP-21_EQU, which seems to be an exact clone of NodOn. However, it requires zigpy/zigpy#1505 in HA to work. zigpy/zigpy#1505 has not yet been released as part of HA as of writing.

Checklist

  • The changes are tested and work correctly
  • pre-commit checks pass / the code has been formatted using Black
  • Tests have been added to verify that the new code works

Copy link

codecov bot commented Sep 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.74%. Comparing base (a5a00d9) to head (c0552b9).
Report is 2 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #3364      +/-   ##
==========================================
+ Coverage   89.72%   89.74%   +0.02%     
==========================================
  Files         319      320       +1     
  Lines       10348    10370      +22     
==========================================
+ Hits         9285     9307      +22     
  Misses       1063     1063              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@TheJulianJES TheJulianJES changed the title NodOn: quirk for SIN-4-FP-21 Add NodOn SIN-4-FP-21 quirk Sep 16, 2024
@TheJulianJES TheJulianJES added the new quirk Adds support for a new device label Sep 16, 2024
@ikruglov ikruglov force-pushed the nodon-pilot-wire branch 3 times, most recently from ed08401 to 228e8a9 Compare September 22, 2024 13:47
@ikruglov
Copy link
Contributor Author

ikruglov commented Sep 22, 2024

@TheJulianJES I've re-pushed commits to use the v2 interface of QuirkBuilder().
Unfortunately, I didn't find an example of how to write tests for it. The tests that I wrote for the previous version didn't work due to compatibility issues, I believe.

If you think tests are necessary here, please give me an example of any test that uses the v2 interface.

@TheJulianJES TheJulianJES added the v2 quirk Quirks using v2 API. Might add custom entities that need translation keys in HA. label Sep 24, 2024
@pounard
Copy link

pounard commented Oct 9, 2024

Any news on this? I would love this to be merged, I myself own a bunch of those modules.

@lboue
Copy link

lboue commented Oct 11, 2024

Please validate. I need this quirk as well

@ikruglov
Copy link
Contributor Author

ikruglov commented Oct 12, 2024

@TheJulianJES, I have a question. I didn't find a better place to ask it.

There is a slight problem with this quirk. An entity added to HA gets the '_none' suffix (see attached picture). I would expect it to get the '_pilot_wire_mode' suffix. Of course, one can rename the HA entity, but getting it right from the start is better.

I did a lot of debugging on my side but got lost, to be honest. I see that the ZHA objects receive the correct _unique_id_suffix value (which was my initial suspicion). But HA's entity still has '_none'.

This problem is not only with an enum but also with a configuration number. I'm preparing a PR to allow configuring impulse mode on NodOn switches (similar to this). My entity also has the _none suffix.

I'd appreciate your help or guidance.

P.S. With no success, I've tried setting translation_key and fallback_name attributes in quirks.

Screenshot 2024-10-12 at 11 09 06

@TheJulianJES
Copy link
Collaborator

The none suffix is caused by the fact that HA doesn't have a value for the translation key. In future versions, HA will fall back to `fallback_name.
When your PR is merged, the quirks bump merged to ZHA, the ZHA bump merged to HA, we'd need to add the translation key to HA then.

@TheJulianJES TheJulianJES added the needs review This PR should be reviewed soon, as it generally looks good. label Oct 15, 2024
@ikruglov
Copy link
Contributor Author

The none suffix is caused by the fact that HA doesn't have a value for the translation key. In future versions, HA will fall back to `fallback_name.

When your PR is merged, the quirks bump merged to ZHA, the ZHA bump merged to HA, we'd need to add the translation key to HA then.

Thank you for the answer. Can you point out where I should add it in HA? I want to try it locally. I will change HA's code in my installation.

@rungeard
Copy link

rungeard commented Nov 6, 2024

Hi @ikruglov thank you so much for this merge request. I'm waiting for it !

@TheJulianJES do you have any idea where this translation key is located, so that @ikruglov could test his code ?

@Fructose38
Copy link

Fructose38 commented Nov 8, 2024

Hi @ikruglov,
Tried to use the quirk, but got this in my home-assistant journal:
nov. 08 18:02:05 <host> hass[370001]: 2024-11-08 18:02:05.296 ERROR (SyncWorker_5) [zhaquirks] Unexpected exception importing custom quirk 'nodon.pilot_wire'
nov. 08 18:02:05 <host> hass[370001]: Traceback (most recent call last):
nov. 08 18:02:05 <host> hass[370001]: File "/var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/__init__.py", line 479, in setup
nov. 08 18:02:05 <host> hass[370001]: spec.loader.exec_module(module)
nov. 08 18:02:05 <host> hass[370001]: File "<frozen importlib._bootstrap_external>", line 995, in exec_module
nov. 08 18:02:05 <host> hass[370001]: File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
nov. 08 18:02:05 <host> hass[370001]: File "/var/lib/private/hass/zhaquirks/nodon/pilot_wire.py", line 5, in <module>
nov. 08 18:02:05 <host> hass[370001]: from zhaquirks.nodon import (
nov. 08 18:02:05 <host> hass[370001]: ImportError: cannot import name 'NodOnPilotWireCluster' from 'zhaquirks.nodon' (/var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/nodon/__init__.py)
Any idea what I might be doing wrong?

@Mookunicorn
Copy link

Hi @ikruglov, Tried to use the quirk, but got this in my home-assistant journal: nov. 08 18:02:05 <host> hass[370001]: 2024-11-08 18:02:05.296 ERROR (SyncWorker_5) [zhaquirks] Unexpected exception importing custom quirk 'nodon.pilot_wire' nov. 08 18:02:05 <host> hass[370001]: Traceback (most recent call last): nov. 08 18:02:05 <host> hass[370001]: File "/var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/__init__.py", line 479, in setup nov. 08 18:02:05 <host> hass[370001]: spec.loader.exec_module(module) nov. 08 18:02:05 <host> hass[370001]: File "<frozen importlib._bootstrap_external>", line 995, in exec_module nov. 08 18:02:05 <host> hass[370001]: File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed nov. 08 18:02:05 <host> hass[370001]: File "/var/lib/private/hass/zhaquirks/nodon/pilot_wire.py", line 5, in <module> nov. 08 18:02:05 <host> hass[370001]: from zhaquirks.nodon import ( nov. 08 18:02:05 <host> hass[370001]: ImportError: cannot import name 'NodOnPilotWireCluster' from 'zhaquirks.nodon' (/var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/nodon/__init__.py) Any idea what I might be doing wrong?

Try this :

"""NODON module for custom device handlers."""
from zigpy.quirks import CustomCluster
from zigpy.quirks.v2 import EntityType, QuirkBuilder
import zigpy.types as t
from zigpy.zcl.foundation import (
    BaseAttributeDefs,
    BaseCommandDefs,
    DataTypeId,
    Direction,
    ZCLAttributeDef,
    ZCLCommandDef,
)
class NodOnPilotWireMode(t.enum8):
    """Pilot wire mode."""
    OFF = 0x00
    COMFORT = 0x01
    ECO = 0x02
    FROST_PROTECTION = 0x03
    COMFORT_MINUS_1 = 0x04
    COMFORT_MINUS_2 = 0x05
NODON = "NodOn"
NODON_PILOT_WIRE_CLUSTER_ID = 0xFC00  # 64512
class NodOnPilotWireCluster(CustomCluster):
    """NodOn manufacturer specific cluster to set Pilot Wire mode."""
    name: str = "NodOnPilotWireCluster"
    cluster_id: t.uint16_t = NODON_PILOT_WIRE_CLUSTER_ID
    ep_attribute: str = "nodon_pilot_wire_cluster"
    class AttributeDefs(BaseAttributeDefs):
        """Attribute definitions."""
        pilot_wire_mode = ZCLAttributeDef(
            id=0x0000,
            type=NodOnPilotWireMode,
            zcl_type=DataTypeId.uint8,
            is_manufacturer_specific=True,
        )
    class ServerCommandDefs(BaseCommandDefs):
        """Server command definitions."""
        set_pilot_wire_mode = ZCLCommandDef(
            id=0x00,
            schema={"mode": NodOnPilotWireMode},
            direction=Direction.Client_to_Server,
            is_manufacturer_specific=True,
        )
(
    QuirkBuilder(NODON, "SIN-4-FP-21")
    .replaces(NodOnPilotWireCluster)
    .enum(
        attribute_name=NodOnPilotWireCluster.AttributeDefs.pilot_wire_mode.name,
        enum_class=NodOnPilotWireMode,
        cluster_id=NodOnPilotWireCluster.cluster_id,
        entity_type=EntityType.STANDARD,
        translation_key="pilot_wire",
        fallback_name="Pilot Wire",
    )
    .add_to_registry()
)

@Fructose38
Copy link

@Mookunicorn this time, I get no error in the logs, but my SIN-4-FP-21 module still shows a on/off switch, no modes alternative.

@Fructose38
Copy link

Fructose38 commented Nov 8, 2024

@Mookunicorn Naming the python file __init__.py seems to have corrected the issue, thanks!

@Fructose38
Copy link

But it looks like my heater does not support other modes, or the module fails to interpret the commands sent :(

@ikruglov
Copy link
Contributor Author

ikruglov commented Nov 8, 2024

Hi @ikruglov, Tried to use the quirk, but got this in my home-assistant journal: nov. 08 18:02:05 <host> hass[370001]: 2024-11-08 18:02:05.296 ERROR (SyncWorker_5) [zhaquirks] Unexpected exception importing custom quirk 'nodon.pilot_wire' nov. 08 18:02:05 <host> hass[370001]: Traceback (most recent call last): nov. 08 18:02:05 <host> hass[370001]: File "/var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/__init__.py", line 479, in setup nov. 08 18:02:05 <host> hass[370001]: spec.loader.exec_module(module) nov. 08 18:02:05 <host> hass[370001]: File "<frozen importlib._bootstrap_external>", line 995, in exec_module nov. 08 18:02:05 <host> hass[370001]: File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed nov. 08 18:02:05 <host> hass[370001]: File "/var/lib/private/hass/zhaquirks/nodon/pilot_wire.py", line 5, in <module> nov. 08 18:02:05 <host> hass[370001]: from zhaquirks.nodon import ( nov. 08 18:02:05 <host> hass[370001]: ImportError: cannot import name 'NodOnPilotWireCluster' from 'zhaquirks.nodon' (/var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/nodon/__init__.py) Any idea what I might be doing wrong?

Make sure that you got file names right. pilot_wire.py can't import classes from init.py (note there is double underscore on each size of 'init').

@Fructose38
Copy link

Hello @ikruglov,
After a thorough reading on the heater I'm using, it actually supports 6 modes of pilot wire, so every mode should work... But I only get Comfort/Frost protection, though HA shows all the modes... Don't know what is messing up with the mode...
I believe the quirk as you provide it should be placed in /var/lib/hass/.venv/lib/python3.12/site-packages/zhaquirks/nodon/ (on my machine), right? Because placing it in my custom_quirks_path produces the error I shown earlier...

@Fructose38
Copy link

@ikruglov In fact, placing the quirk in the correct path made everything work as expected. I'm now a happy 6 modes pilot wire user :)

@ikruglov
Copy link
Contributor Author

@TheJulianJES I've addressed your suggestions except for the enum. Let me know what you want to see there.

@TheJulianJES TheJulianJES added the needs reviewer answer An answer from a reviewer is needed (e.g. why a PR isn't acceptable in the current state). label Nov 26, 2024
@TheJulianJES
Copy link
Collaborator

After some discussion, we might need to implement it as a climate entity, similar to the screenshots seen in the description of #3031. Since we can't expose climate devices via quirks v2, we'd need to partly implement this in ZHA.

I'm aware this is annoying, but can we remove the enum/select entity for now?
We could merge this and make the HA 2024.12 release then. With that, you could at least write the attribute manually or using some third-party components (like zha-toolkit), until support is properly implemented (or there's a decision to just implement it as select entity, like Z2M does).

@TheJulianJES TheJulianJES changed the title Add NodOn SIN-4-FP-21 (Adeo SIN-4-FP-21_EQU) quirk Add initial NodOn SIN-4-FP-21 (Adeo SIN-4-FP-21_EQU) quirk Nov 26, 2024
@TheJulianJES TheJulianJES removed needs review This PR should be reviewed soon, as it generally looks good. needs reviewer answer An answer from a reviewer is needed (e.g. why a PR isn't acceptable in the current state). labels Nov 26, 2024
@TheJulianJES
Copy link
Collaborator

Per my previous comment, I've removed the enum entity for now.
We can at least expose the additional Zigbee attributes by merging this initial version of a quirk then.

It needs to be discussed how the heating mode is handled: presets in a HA climate entity or just a select entity like Zigbee2MQTT provides for devices like this.

Copy link
Collaborator

@TheJulianJES TheJulianJES left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And of course, thanks for the PR!
I hope we can implement an entity for the wire mode soon. For now, you'll at least have some initial support by directly writing to the attribute.

@TheJulianJES TheJulianJES merged commit 8c68870 into zigpy:dev Nov 26, 2024
9 checks passed
@TheJulianJES
Copy link
Collaborator

Opened the following issue for tracking and so this is not forgotten about:

@ikruglov
Copy link
Contributor Author

ikruglov commented Nov 27, 2024

@TheJulianJES. Thanks for merging, but it's unfortunate that you removed the enum as it makes automation harder.

I'm willing to investigate necessary changes in HA if you can tell me what is needed and give me some guidance.

@ikruglov ikruglov deleted the nodon-pilot-wire branch November 27, 2024 09:45
@TheJulianJES
Copy link
Collaborator

TheJulianJES commented Nov 27, 2024

It also really wasn't what I wanted, but we have to make sure to adhere to Home Assistant standards. We'll follow up in the issue on what to do next.

Just thought it would be useful to get some initial support for the attribute at least. Quirks v2 adding entities is relatively new and it's uncommon for something like this to work with HA. We just have to double check we're doing everything correctly.
In this case, we might need to implement the heating modes as present on a climate entity that's created in ZHA. Again, I'll follow up in the issue, hopefully relatively soon.

@ofthesun9
Copy link

many thanks @ikruglov for proposing this PR. I am currently using the quirk defined in this comment.

I don't get what will happen when my home-assistant container will be upgraded to the december version:
can I keep the quirk and still get the enum definition and have the "select" available and working ?

@ikruglov
Copy link
Contributor Author

@ofthesun9, it depends on how you set it up. I run HA in a docker container and mount modified files on top of original ones. So, in this case, there will be no change. If your setup is different I can't comment then.

@TheJulianJES
Copy link
Collaborator

TheJulianJES commented Nov 29, 2024

You can keep using the custom quirk with the select entity.
However, due to a bug in zigpy in connection with v2 quirks, you'll need to add the following lines to the custom quirk when using Home Assistant Core 2024.12:

from zigpy.quirks import DEVICE_REGISTRY
# other existing imports from custom quirk

# remove upstream quirk from registry
del DEVICE_REGISTRY._registry_v2[("NodOn", "SIN-4-FP-21")]

# original custom quirk code here

For more details on the issue, see: zigpy/zigpy#1508
Custom quirks should always take priority over built-in ones. They do for "v1" quirks, but it doesn't properly work with v2 quirks, yet.

(This is not required if the original library files are modified, but that's not as easy to do and could break things. The custom quirks config + path is recommended in general.)

@ofthesun9
Copy link

thanks @TheJulianJES and @ikruglov for your help.
I have now Home Assistant Core 2024.12.0 running with a custom quirk.
For reference, hereafter the main steps:

Enabling custom quirks in config/configuration.yaml

zha:
  custom_quirks_path: /config/custom_zha_quirks/
  enable_quirks: true

Adding two files in config/custom_zha_quirks/nodon
$ cat config/custom_zha_quirks/nodon/__init__.py
"""NODON module for custom device handlers."""

$ cat config/custom_zha_quirks/nodon/pilot_wire.py

"""NodOn pilot wire heating module."""
from zigpy.quirks import DEVICE_REGISTRY
# other existing imports from custom quirk

# remove upstream quirk from registry
del DEVICE_REGISTRY._registry_v2[("NodOn", "SIN-4-FP-21")]

from zigpy.quirks import CustomCluster
from zigpy.quirks.v2 import EntityType, QuirkBuilder
import zigpy.types as t
from zigpy.zcl.foundation import BaseAttributeDefs, DataTypeId, ZCLAttributeDef

NODON = "NodOn"
NODON_MANUFACTURER_ID = 4747
NODON_PILOT_WIRE_CLUSTER_ID = 0xFC00  # 64512
ADEO = "Adeo"


class NodOnPilotWireMode(t.enum8):
    """Pilot wire mode."""

    # Codes taken from
    # https://github.com/Koenkk/zigbee-herdsman-converters/blob/0f4833340a20db3dae625a61c41d9be0a6f952be/src/converters/fromZigbee.ts#L5285.

    Off = 0x00
    Comfort = 0x01
    Eco = 0x02
    FrostProtection = 0x03
    ComfortMinus1 = 0x04
    ComfortMinus2 = 0x05


class NodOnPilotWireCluster(CustomCluster):
    """NodOn manufacturer specific cluster to set Pilot Wire mode."""

    name: str = "PilotWireCluster"
    cluster_id: t.uint16_t = NODON_PILOT_WIRE_CLUSTER_ID
    ep_attribute: str = "pilot_wire_cluster"

    class AttributeDefs(BaseAttributeDefs):
        """Attribute definitions."""

        pilot_wire_mode = ZCLAttributeDef(
            id=0x0000,
            type=NodOnPilotWireMode,
            # need to explicitly set ZCL type
            zcl_type=DataTypeId.uint8,
            is_manufacturer_specific=True,
        )


class AdeoPilotWireCluster(NodOnPilotWireCluster):
    """Adeo manufacturer specific cluster to set Pilot Wire mode."""

    # Adeo SIN-4-FP-21_EQU has a weird setup where it reports 4727
    # manufacturer_code in node_descriptor(), but requires NodOn's (4747)
    # manufacturer_id to execute commands and get/set attributes.
    manufacturer_id_override: t.uint16_t = NODON_MANUFACTURER_ID


nodon = (
    QuirkBuilder(NODON, "SIN-4-FP-21")
    .replaces(NodOnPilotWireCluster)
    .enum(
        attribute_name=NodOnPilotWireCluster.AttributeDefs.pilot_wire_mode.name,
        enum_class=NodOnPilotWireMode,
        cluster_id=NodOnPilotWireCluster.cluster_id,
        entity_type=EntityType.STANDARD,
        translation_key="pilot_wire",
        fallback_name="Pilot wire",
    )
)  # fmt: skip

adeo = (
    nodon.clone(omit_man_model_data=True)
    .applies_to(ADEO, "SIN-4-FP-21_EQU")
    .replaces(AdeoPilotWireCluster)
)

nodon.add_to_registry()
adeo.add_to_registry()

@Skunnyk
Copy link

Skunnyk commented Dec 6, 2024

Thank you, nice and clean workaround waiting for full upstream integration :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new quirk Adds support for a new device v2 quirk Quirks using v2 API. Might add custom entities that need translation keys in HA.
Projects
None yet
Development

Successfully merging this pull request may close these issues.