zigpy / zha-device-handlers

ZHA device handlers bridge the functionality gap created when manufacturers deviate from the ZCL specification, handling deviations and exceptions by parsing custom messages to and from Zigbee devices.
Apache License 2.0
696 stars 640 forks source link

[Device Support Request] Amina S EV charger #3122

Open VVVVVVVip opened 3 months ago

VVVVVVVip commented 3 months ago

Problem description

New cool charger for electric cars that is independent of cloud connections and other third party solutions and instead can be controlled thru Zigbee.

It connects nicely but only with the most basic functions. One switch to enable/disable charging and four sensors for Watt, Ampere, Volt and Hertz. Several other endpoints and clusters are identified when i look in "manage this device" but no entities are created. bild

From manufacture documentation Endpoint 242 Green Energy is not used for now.

How Amina S behaves in a Zigbee network Amina S is an routing (end) device. Amina S implements 3 endpoints with multiple clusters, see below. At manufacturing time each Amina S will be given a unique install code which is printed labels both on the device and in the user guide. The install code is available in printed format and also machine readable (QR-code). The manufacturing code for Amina Distribution AS is 0x143B.

Manufactures zigbee documentation; https://doc.clickup.com/9004130215/p/h/8cb07x7-18308/c750786359b035a

> Tech savvy amina S customers have used our Zigbee documentation to set up their own automations in Home Assistant. Some have also published their work for others to use, such as a Zigbee2MQTT converter. See post 2

Amina S website https://aminacharging.com/products/amina-s/

Solution description

Add Amina special clusters Amina S Control (0xFEE7) in linked documentation, and the other clusters. Cluster for Level Control (0x0008) is in my opinion the most interesting as it makes it possible to control and adapt the charging current .

bild

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device signature

Device signature ```json { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4660, maximum_buffer_size=108, maximum_incoming_transfer_size=1617, server_mask=11264, maximum_outgoing_transfer_size=1617, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)", "endpoints": { "5": { "profile_id": "0x0104", "device_type": "0xfff0", "input_clusters": [ "0x0000" ], "output_clusters": [ "0x0019" ] }, "10": { "profile_id": "0x0104", "device_type": "0x0003", "input_clusters": [ "0x0000", "0x0003", "0x0006", "0x0008", "0x0b04", "0xfee7" ], "output_clusters": [] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "amina distribution AS", "model": "amina S", "class": "zigpy.device.Device" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2024.4.4", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.12.2", "docker": true, "arch": "x86_64", "timezone": "Europe/Stockholm", "os_name": "Linux", "os_version": "6.6.25-haos", "supervisor": "2024.04.0", "host_os": "Home Assistant OS 12.2", "docker_version": "25.0.5", "chassis": "vm", "run_as_root": true }, "custom_components": { "stihl_imow": { "version": "1.0.0", "requirements": [ "imow-webapi==0.8.4" ] }, "hacs": { "version": "1.34.0", "requirements": [ "aiogithubapi>=22.10.1" ] }, "skolmat": { "version": "1.5.0", "requirements": [ "feedparser>=6.0.0", "beautifulsoup4>=4.12.2", "python-dateutil>=2.8.2" ] }, "swemail": { "version": "1.0.7", "requirements": [] }, "measureit": { "version": "0.5.0", "requirements": [ "croniter==2.0.2" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "after_dependencies": [ "onboarding", "usb" ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly", "@TheJulianJES" ], "config_flow": true, "dependencies": [ "file_upload" ], "documentation": "https://www.home-assistant.io/integrations/zha", "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp", "universal_silabs_flasher" ], "requirements": [ "bellows==0.38.1", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.114", "zigpy-deconz==0.23.1", "zigpy==0.63.5", "zigpy-xbee==0.20.1", "zigpy-zigate==0.12.0", "zigpy-znp==0.12.1", "universal-silabs-flasher==0.0.18", "pyserial-asyncio-fast==0.11" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "10C4", "pid": "EA60", "description": "*slzb-07*", "known_devices": [ "smlight slzb-07" ] }, { "vid": "1A86", "pid": "55D4", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "vid": "10C4", "pid": "EA60", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus" ] }, { "vid": "10C4", "pid": "EA60", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*zigstar*", "known_devices": [ "ZigStar Coordinators" ] }, { "vid": "1CF1", "pid": "0030", "description": "*conbee*", "known_devices": [ "Conbee II" ] }, { "vid": "0403", "pid": "6015", "description": "*conbee*", "known_devices": [ "Conbee III" ] }, { "vid": "10C4", "pid": "8A2A", "description": "*zigbee*", "known_devices": [ "Nortek HUSBZB-1" ] }, { "vid": "0403", "pid": "6015", "description": "*zigate*", "known_devices": [ "ZiGate+" ] }, { "vid": "10C4", "pid": "EA60", "description": "*zigate*", "known_devices": [ "ZiGate" ] }, { "vid": "10C4", "pid": "8B34", "description": "*bv 2010/10*", "known_devices": [ "Bitron Video AV2010/10" ] } ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_uzg-01._tcp.local.", "name": "uzg-01*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 55075, "manufacturer": "amina distribution AS", "model": "amina S", "name": "amina distribution AS amina S", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "quirk_id": null, "manufacturer_code": 4660, "power_source": "Mains", "lqi": 87, "rssi": -83, "last_seen": "2024-04-29T23:03:36", "available": true, "device_type": "Router", "signature": { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4660, maximum_buffer_size=108, maximum_incoming_transfer_size=1617, server_mask=11264, maximum_outgoing_transfer_size=1617, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)", "endpoints": { "10": { "profile_id": "0x0104", "device_type": "0x0003", "input_clusters": [ "0x0000", "0x0003", "0x0006", "0x0008", "0x0b04", "0xfee7" ], "output_clusters": [] }, "5": { "profile_id": "0x0104", "device_type": "0xfff0", "input_clusters": [ "0x0000" ], "output_clusters": [ "0x0019" ] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "amina distribution AS", "model": "amina S" }, "active_coordinator": false, "entities": [ { "entity_id": "button.zb_020_b_x1p_amina_identifiera", "name": "amina distribution AS amina S" }, { "entity_id": "sensor.zb_020_b_x1p_amina_strom", "name": "amina distribution AS amina S" }, { "entity_id": "sensor.zb_020_b_x1p_amina_spanning", "name": "amina distribution AS amina S" }, { "entity_id": "sensor.zb_020_b_x1p_amina_ac_frekvens", "name": "amina distribution AS amina S" }, { "entity_id": "sensor.zb_020_b_x1p_amina_effekt", "name": "amina distribution AS amina S" }, { "entity_id": "switch.zb_020_b_x1p_amina_brytare", "name": "amina distribution AS amina S" }, { "entity_id": "update.zb_020_b_x1p_amina_firmware", "name": "amina distribution AS amina S" } ], "neighbors": [ { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x71F4", "permit_joining": "NotAccepting", "depth": "0", "lqi": "64" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xD2DB", "permit_joining": "NotAccepting", "depth": "0", "lqi": "56" }, { "device_type": "Coordinator", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x0000", "permit_joining": "NotAccepting", "depth": "0", "lqi": "44" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x17A7", "permit_joining": "NotAccepting", "depth": "1", "lqi": "108" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x29EE", "permit_joining": "NotAccepting", "depth": "0", "lqi": "32" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x57FE", "permit_joining": "NotAccepting", "depth": "2", "lqi": "76" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x274C", "permit_joining": "NotAccepting", "depth": "0", "lqi": "56" } ], "routes": [], "endpoint_names": [ { "name": "LEVEL_CONTROLLABLE_OUTPUT" }, { "name": "undefined_0xfff0" }, { "name": "PROXY_BASIC" } ], "user_given_name": "ZB.020.B.x1P-Amina", "device_reg_id": "5a24467f6969cb46461f6d90ab5657f9", "area_id": "ute", "cluster_details": { "10": { "device_type": { "name": "LEVEL_CONTROLLABLE_OUTPUT", "id": 3 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0004": { "attribute_name": "manufacturer", "value": "amina distribution AS" }, "0x0005": { "attribute_name": "model", "value": "amina S" }, "0x0007": { "attribute_name": "power_source", "value": 2 }, "0x4000": { "attribute_name": "sw_build_id", "value": "1.5.3" } }, "unsupported_attributes": { "0x0012": { "attribute_name": "device_enabled" }, "0x000b": { "attribute_name": "product_url" } } }, "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0xfffd": { "attribute_name": "cluster_revision", "value": 2 }, "0x0000": { "attribute_name": "on_off", "value": 1 } }, "unsupported_attributes": { "0x4001": { "attribute_name": "on_time" }, "0x4002": { "attribute_name": "off_wait_time" }, "0x4003": { "attribute_name": "start_up_on_off" } } }, "0x0008": { "endpoint_attribute": "level", "attributes": { "0x0000": { "attribute_name": "current_level", "value": 6 }, "0x0003": { "attribute_name": "max_level", "value": 6 }, "0x0002": { "attribute_name": "min_level", "value": 6 } }, "unsupported_attributes": { "0x4000": { "attribute_name": "start_up_current_level" }, "0x0012": { "attribute_name": "on_transition_time" }, "0x0013": { "attribute_name": "off_transition_time" }, "0xfffe": { "attribute_name": "reporting_status" }, "0x0014": { "attribute_name": "default_move_rate" }, "0x0010": { "attribute_name": "on_off_transition_time" }, "0x0011": { "attribute_name": "on_level" } } }, "0x0b04": { "endpoint_attribute": "electrical_measurement", "attributes": { "0x0603": { "attribute_name": "ac_current_divisor", "value": 1000 }, "0x0602": { "attribute_name": "ac_current_multiplier", "value": 1 }, "0x0300": { "attribute_name": "ac_frequency", "value": 499 }, "0x0401": { "attribute_name": "ac_frequency_divisor", "value": 10 }, "0x0400": { "attribute_name": "ac_frequency_multiplier", "value": 1 }, "0x0605": { "attribute_name": "ac_power_divisor", "value": 1 }, "0x0604": { "attribute_name": "ac_power_multiplier", "value": 1 }, "0x0601": { "attribute_name": "ac_voltage_divisor", "value": 1 }, "0x0600": { "attribute_name": "ac_voltage_multiplier", "value": 1 }, "0x050b": { "attribute_name": "active_power", "value": 0 }, "0x090b": { "attribute_name": "active_power_ph_b", "value": 0 }, "0x0a0b": { "attribute_name": "active_power_ph_c", "value": 0 }, "0x0000": { "attribute_name": "measurement_type", "value": 57 }, "0x0403": { "attribute_name": "power_divisor", "value": 1000 }, "0x0402": { "attribute_name": "power_multiplier", "value": 1 }, "0x0508": { "attribute_name": "rms_current", "value": 0 }, "0x0908": { "attribute_name": "rms_current_ph_b", "value": 0 }, "0x0a08": { "attribute_name": "rms_current_ph_c", "value": 0 }, "0x0505": { "attribute_name": "rms_voltage", "value": 229 }, "0x0905": { "attribute_name": "rms_voltage_ph_b", "value": 0 }, "0x0a05": { "attribute_name": "rms_voltage_ph_c", "value": 0 }, "0x0304": { "attribute_name": "total_active_power", "value": 0 } }, "unsupported_attributes": { "0x0302": { "attribute_name": "ac_frequency_max" }, "0x0510": { "attribute_name": "power_factor" }, "0x0507": { "attribute_name": "rms_voltage_max" }, "0x050d": { "attribute_name": "active_power_max" }, "0x050a": { "attribute_name": "rms_current_max" }, "0x050f": { "attribute_name": "apparent_power" } } }, "0xfee7": { "endpoint_attribute": "manufacturer_specific", "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": {} }, "5": { "device_type": { "name": "undefined_0xfff0", "id": 65520 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": { "0x0019": { "endpoint_attribute": "ota", "attributes": { "0x0002": { "attribute_name": "current_file_version", "value": 17104899 } }, "unsupported_attributes": {} } } }, "242": { "device_type": { "name": "PROXY_BASIC", "id": 97 }, "profile_id": 41440, "in_clusters": {}, "out_clusters": { "0x0021": { "endpoint_attribute": "green_power", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Logs

Logs ```python [Paste the logs here] ```

Custom quirk

Custom quirk ```python [Paste your custom quirk here] ```

Additional information

No response

VVVVVVVip commented 3 months ago

Here is a Zigbee2MQTT Converter, could this be applied in ZHA? https://github.com/somlioy/amina_s

*fixed link

VVVVVVVip commented 3 months ago

Tested some more and updated first post with more information, diagnostic and signature.

VVVVVVVip commented 2 months ago

In my way trying to find some way forward, I tried to chat with Google Gemini. My first try with AI. Can someone tell if the result is even useful somehow? This is the code it generated for cluster: Level Control (0x0008) (but it looks like the formatting changes when copied here)

from zigpy.endpoints import BaseEndpoint
from zigpy.clusters import LevelControlCluster

class AminaSLevelControlCluster(LevelControlCluster):
  """Custom cluster implementation for Level Control on Amina S device."""

  attribute_map = {
      # Map attribute IDs from the device to ZHA attributes
      # This section might require adjustments based on the diagnostic info
      0x0000: 'current_level',  # Replace with the actual attribute ID for current level
      # Add entries for other attributes based on diagnostic information
  }

  @property
  def current_level(self):
      """Read the current level from the device and convert it to a ZHA format."""
      # Replace this with logic to read the current level from the device
      # Refer to explanations in the previous code block.
      pass

  def set_level(self, level):
      """Send a command to the device to set the target level."""
      # Replace this with logic to send a Zigbee command to set the level
      # Refer to explanations in the previous code block.
      pass

  # Implement methods for the additional attributes based on their functionalities:
  @property
  def remaining_time_to_level(self):
      """Read the remaining time to reach the target level (if supported)."""
      # Replace with logic to read the remaining time data from the device
      # This might not be supported by the Amina S device, check documentation.
      # Consult Zigbee Cluster Library specification for the format of the data.
      pass

  def move_to_level(self, level, transition_time):
      """Send a command to move to a target level with a transition time."""
      # Replace with logic to send a Zigbee command for move_to_level
      # Refer to Zigbee Cluster Library specification for the command format.
      pass

  # Similar methods for other attributes (replace placeholders with actual logic):
  @property
  def on_level(self):
      pass

  @property
  def on_off_transition_time(self):
      pass

  @property
  def ramp_rate(self):
      pass

  def step_with_on_off(self, updown, amount, transition_time):
      pass

  def stop_move(self):
      pass

class AminaSDevice(BaseEndpoint):
  """Basic device class for the Amina S device."""

  # Define other clusters and functionality based on your analysis of the diagnostic information

Comment from Gemini.

Explanation of Added Functionality:

We've added properties and methods for the seven attributes listed under Command Support: remaining_time_to_level: This reads the remaining time to reach the target level (if supported). You'll need to check the diagnostic information and potentially the Amina S documentation to see if this attribute is actually reported by the device. move_to_level: This sends a command to move to a target level with a transition time. You'll need to implement this based on the Zigbee Cluster Library specification for the move_to_level command. Similar methods are included for the other attributes. However, these are currently placeholders, and you'll need to fill them in with logic to: Read the data from the device (if supported) using appropriate Zigbee library methods. Convert the data to a format suitable for Home Assistant attributes (refer to ZHA documentation).

Important Note: The provided information (diagnostic information and Amina S documentation pages 2-15) might not be sufficient to fully implement all these functionalities. You might need to consult additional resources or the device manufacturer for specifics. Remember to adjust the code further based on the actual attribute IDs and data formats identified through the diagnostic information.

I hope this expanded version helps you get started with writing the quirk for the Amina S Level Control cluster!_

dennisyakkz commented 1 month ago

I have added the charger in ZHA.

Inside the "Manage device" function in ZHA all relevant endpoints and commands are exposed. I can query charging state, power consumption. Using the commands it's also possible to control the charger (start/stop, specify max charging power) However these endpoints/commands are not exposed in the generated home assistant device.

In order to make best use of the device the following endpoints and commands need to be exposed:

Commands:

Endpoints: energy usage:

statistics:

Desirable:

I can help out in engineering, testing and technical stuff. But I'm zero at building the quirk itself.

If anyone, who resides in the Netherlands, is upto developing the actual quirk so that the device is properly supported in Home Assistant. I'm able to donate a device for that.