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

[Device Support Request] Ikea Starkvind Air Purifier #1215

Closed
kcleong opened this issue Dec 13, 2021 · 30 comments
Closed

[Device Support Request] Ikea Starkvind Air Purifier #1215

kcleong opened this issue Dec 13, 2021 · 30 comments

Comments

@kcleong
Copy link

kcleong commented Dec 13, 2021

Is your feature request related to a problem? Please describe.
N/A

Describe the solution you'd like

When adding the Starkvind in ZHA/home assistant only the fan is recognized. The device also support other sensors but those are not exposed. Would it be possible to also expose the other sensors which are supported by this device; pm25, air_quality, led_enable, child_lock, replace_filter.

Device signature - this can be acquired by removing the device from ZHA and pairing it again from the add devices screen. Be sure to add the entire content of the log panel after pairing the device to a code block below this line.

{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, manufacturer_code=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0007",
      "in_clusters": [
        "0x0000",
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0202",
        "0xfc57",
        "0xfc7d"
      ],
      "out_clusters": [
        "0x0019",
        "0x0400",
        "0x042a"
      ]
    },
    "242": {
      "profile_id": 41440,
      "device_type": "0x0061",
      "in_clusters": [],
      "out_clusters": [
        "0x0021"
      ]
    }
  },
  "manufacturer": "IKEA of Sweden",
  "model": "STARKVIND Air purifier",
  "class": "zigpy.device.Device"
}
New device 0x4d79 (b4:e3:f9:ff:fe:2b:07:3f) joined the network
[0x4d79] Scheduling initialization
Received frame on uninitialized device <Device model=None manuf=None nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=False> from ep 0 to ep 0, cluster ZDOCmd.Device_annce: b'\xFF\x79\x4D\x3F\x07\x2B\xFE\xFF\xF9\xE3\xB4\x8E'
[0x4d79:zdo] ZDO request ZDOCmd.Device_annce: [0x4D79, b4:e3:f9:ff:fe:2b:07:3f, 142]
Tries remaining: 3
[0x4d79] Requesting 'Node Descriptor'
Tries remaining: 2
[0x4d79] Extending timeout for 0x13 request
Received frame on uninitialized device <Device model=None manuf=None nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=False> from ep 0 to ep 0, cluster ZDOCmd.Node_Desc_rsp: b'\x13\x00\x79\x4D\x01\x40\x8E\x7C\x11\x52\x52\x00\x00\x2C\x52\x00\x00'
[0x4d79] Got Node Descriptor: NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, manufacturer_code=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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)
[0x4d79] Discovering endpoints
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=False> from ep 0 to ep 0, cluster ZDOCmd.Active_EP_rsp: b'\x14\x00\x79\x4D\x02\x01\xF2'
[0x4d79] Discovered endpoints: [1, 242]
[0x4d79] Initializing endpoints [<Endpoint id=1 in=[] out=[] status=<Status.NEW: 0>>, <Endpoint id=242 in=[] out=[] status=<Status.NEW: 0>>]
[0x4d79:1] Discovering endpoint information
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=False> from ep 0 to ep 0, cluster ZDOCmd.Simple_Desc_rsp: b'\x15\x00\x79\x4D\x1C\x01\x04\x01\x07\x00\x01\x07\x00\x00\x03\x00\x04\x00\x05\x00\x02\x02\x57\xFC\x7D\xFC\x03\x19\x00\x00\x04\x2A\x04'
[0x4d79:1] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=7, device_version=1, input_clusters=[0, 3, 4, 5, 514, 64599, 64637], output_clusters=[25, 1024, 1066])
[0x4d79:242] Discovering endpoint information
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=False> from ep 0 to ep 0, cluster ZDOCmd.Simple_Desc_rsp: b'\x16\x00\x79\x4D\x0A\xF2\xE0\xA1\x61\x00\x00\x00\x01\x21\x00'
[0x4d79:242] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=242, profile=41440, device_type=97, device_version=0, input_clusters=[], output_clusters=[33])
[0x4d79:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=23 command_id=Command.Read_Attributes_rsp>
[0x4d79] Read model 'STARKVIND Air purifier' and manufacturer 'IKEA of Sweden' from <Endpoint id=1 in=[basic:0x0000, identify:0x0003, groups:0x0004, scenes:0x0005, fan:0x0202, manufacturer_specific:0xFC57, manufacturer_specific:0xFC7D] out=[ota:0x0019, illuminance:0x0400, pm25:0x042A] status=<Status.ZDO_INIT: 1>>
[0x4d79] Discovered basic device information for <Device model='STARKVIND Air purifier' manuf='IKEA of Sweden' nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=True>
Device is initialized <Device model='STARKVIND Air purifier' manuf='IKEA of Sweden' nwk=0x4D79 ieee=b4:e3:f9:ff:fe:2b:07:3f is_initialized=True>
Checking quirks for IKEA of Sweden STARKVIND Air purifier (b4:e3:f9:ff:fe:2b:07:3f)
Considering <class 'zhaquirks.ikea.tradfriplug.TradfriPlug'>
Fail because endpoint list mismatch: {1, 2, 242} {1, 242}
Considering <class 'zhaquirks.xbee.xbee_io.XBeeSensor'>
Fail because endpoint list mismatch: {232, 230} {1, 242}
Considering <class 'zhaquirks.xbee.xbee3_io.XBee3Sensor'>
Fail because endpoint list mismatch: {232, 230} {1, 242}
Considering <class 'zhaquirks.smartthings.tag_v4.SmartThingsTagV4'>
Fail because endpoint list mismatch: {1} {1, 242}
Considering <class 'zhaquirks.smartthings.multi.SmartthingsMultiPurposeSensor'>
Fail because endpoint list mismatch: {1} {1, 242}
Considering <class 'zhaquirks.netvox.z308e3ed.Z308E3ED'>
Fail because endpoint list mismatch: {1} {1, 242}
Considering <class 'zhaquirks.gledopto.soposhgu10.SoposhGU10'>
Fail because endpoint list mismatch: {11, 13} {1, 242}
Considering <class 'bellows.zigbee.application.EZSPCoordinator'>
Fail because endpoint list mismatch: {1} {1, 242}
device - 0x4D79:b4:e3:f9:ff:fe:2b:07:3f entering async_device_initialized - is_new_join: True
device - 0x4D79:b4:e3:f9:ff:fe:2b:07:3f has joined the ZHA zigbee network
[0x4D79](STARKVIND Air purifier): started configuration
[0x4D79:ZDO](STARKVIND Air purifier): 'async_configure' stage succeeded
[0x4D79:1:0x0000]: finished channel configuration
[0x4D79:1:0x0019]: finished channel configuration
Error handling '_save_attribute' event with (b4:e3:f9:ff:fe:2b:07:3f, 1, 0, 4, 'IKEA of Sweden') params: FOREIGN KEY constraint failed
Error handling '_save_attribute' event with (b4:e3:f9:ff:fe:2b:07:3f, 1, 0, 5, 'STARKVIND Air purifier') params: FOREIGN KEY constraint failed
[0x4D79:1:0x0202]: bound 'fan' cluster: Status.SUCCESS
[0x4d79:1:0x0202] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=25 command_id=Command.Configure_Reporting_rsp>
[0x4D79:1:0x0202]: Successfully configured reporting for '{'fan_mode': (5, 900, 1)}' on 'fan' cluster: [ConfigureReportingResponseRecord(status=0)]
[0x4D79:1:0x0202]: finished channel configuration
[0x4D79:1:0x0202]: 'async_configure' stage succeeded
[0x4D79:1:0x0000]: 'async_configure' stage succeeded
[0x4D79:1:0x0019]: 'async_configure' stage succeeded
[0x4D79](STARKVIND Air purifier): completed configuration
[0x4D79](STARKVIND Air purifier): stored in registry: ZhaDeviceEntry(name='IKEA of Sweden STARKVIND Air purifier', ieee='b4:e3:f9:ff:fe:2b:07:3f', last_seen=1639420791.6012802)
[0x4d79:1:0x0003] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=26 command_id=Command.Default_Response>
[0x4D79:1:0x0003]: executed 'trigger_effect' command with args: '(2, 0)' kwargs: '{}' result: [64, <Status.SUCCESS: 0>]
[0x4D79](STARKVIND Air purifier): started initialization
[0x4D79:ZDO](STARKVIND Air purifier): 'async_initialize' stage succeeded
[0x4D79:1:0x0202]: initializing channel: from_cache: False
[0x4D79:1:0x0000]: initializing channel: from_cache: False
[0x4D79:1:0x0000]: finished channel initialization
[0x4D79:1:0x0019]: initializing channel: from_cache: False
[0x4D79:1:0x0019]: finished channel initialization
[0x4d79:1:0x0202] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=27 command_id=Command.Read_Attributes_rsp>
[0x4D79:1:0x0202]: Attribute report 'Fan Control'[fan_mode_sequence] = FanModeSequence.Low_Med_High_Auto
[0x4d79:1:0x0202] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=28 command_id=Command.Read_Attributes_rsp>
[0x4D79:1:0x0202]: Attribute report 'Fan Control'[fan_mode] = FanMode.Off
[0x4D79:1:0x0202]: finished channel initialization
[0x4D79:1:0x0202]: 'async_initialize' stage succeeded
[0x4D79:1:0x0000]: 'async_initialize' stage succeeded
[0x4D79:1:0x0019]: 'async_initialize' stage succeeded
[0x4D79](STARKVIND Air purifier): power source: Mains
[0x4D79](STARKVIND Air purifier): completed initialization

Additional context
Add any other context or screenshots about the feature request here.

Screenshot 2021-12-13 19 43 15

@Adminiuga
Copy link
Contributor

If you run grep -i 0x4D79 home-assistant.log what do you see from Ikea purifier?

@kcleong
Copy link
Author

kcleong commented Dec 14, 2021

@Adminiuga please see the logs I've pasted in this gist: https://gist.github.com/kcleong/e2f4f3a2b57f6115e613b479f9257a3e

@Adminiuga
Copy link
Contributor

I wasn't looking for the device join logs. Need to see what information device sends when idle. Join it and leave running for 24 hours. Send the logs with all the traffic from the device

@kcleong
Copy link
Author

kcleong commented Dec 16, 2021

@LennyPenny
Copy link

I also own such a device now and can provide additional logs if needed @Adminiuga

@LennyPenny
Copy link

here's some detailed info: dresden-elektronik/deconz-rest-plugin#5351 (comment)

@br-edward
Copy link

I also own the device and would love to see support. Especially the raw pm25 and fan_speeds / adjustments. Thank you!!

@An4cl3t0
Copy link

An4cl3t0 commented Mar 7, 2022

I can also help for integration (no experience).

@juliansteenbakker
Copy link

I would love to help as well. I bought a Starkvind a few days ago and am able to control the fan with ZHA. It seems like all the functionality is integrated in Zigbee2MQTT and i was thinking, isn't there a guide on how to convert a Zigbee2MQTT integration to ZHA integration? It's been a few years since i last touched python so i need to refresh my memory a bit but i'm trying to get something working but a guide or some other documentation on how to write a quirk would be great.

@MattWestb
Copy link
Contributor

Z2M dont care of how the device is on the "hardware" side but you cant tracking the functions they is using for the device and see h´what they is doing and trying implementing it in the quirk.

First you must making one signature and all imports for getting it working = ZHA is loading the quirk, best looking on this guide https://github.com/zigpy/zha-device-handlers#what-the-heck-is-a-quirk.
Then you have the signature working and putting the the same cluster on the endpoints in the replacement.
Then its working you shall one working quirks without extras and can trying implanting functions.

Best is looking on one other devices that have similar function and and copy it to your quirk.
If you can adding little more functions (like fan speed with auto mode) is all better then only standard Zigbee functions fir the device.

You can also looking how deCONZ have made the implementation but tits working very different but some interesting then there discussion how implanting functions.

If some more user is having the device and can coding or other user with knowledge its normally going well but can taking time getting all in place.

The good thing is that IKEA is doing most thing pity standard Zigbee and i think its only the PPM and fan speed auto that is little tricky. I think its having function for filter time and then it shall being exchanged.

I dont have the device and i cant doing coding so i cant helping so much but can making commands and sharing little knowledge.

@stofakiller
Copy link

Hi,
Ijust found this ticket and i have a stormvind. And got both deconz and zha (2 sticks) installed i my HA... And its correct doconz and zha are very different. I think ZHA is the best, but the percentage_step is wrong is has to be 20%, because it has 5 steps on the knob beside the auto setting. Im very good at HA, but i havent figured out yet to edit/change devices yet. So if someone could point me in a right direction im glad to contribute :-)

@mitaka
Copy link

mitaka commented May 9, 2022

I have the same device. I can help with debugging adding missing information if someone points me to what have to be done.

@arnemauer
Copy link
Contributor

arnemauer commented May 27, 2022

I've started with a quirk :)

"""Device handler for IKEA of Sweden STARKVIND Air purifier."""
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import (
    Basic,
    Groups,
    Identify,
    Ota,
    PollControl,
    Scenes,
    GreenPowerProxy,
)
from zigpy.zcl.clusters.hvac import Fan
from zhaquirks.ikea import IKEA
from zigpy.zcl.clusters.measurement import (
    IlluminanceMeasurement,
    PM25,
)

from zhaquirks.const import (
    CLUSTER_ID,
    DEVICE_TYPE,
    ENDPOINT_ID,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

#IKEA_CLUSTER_ID = 0xFC7C  # decimal = 64636

WWAH_CLUSTER_ID = 0xFC57  # decimal = 64599
IKEA_CLUSTER_UNKNOWN_ID = 0xFC7D

class IkeaSTARKVIND(CustomDevice):
    """STARKVIND Air purifier by IKEA of Sweden."""

    signature = {
        # <SimpleDescriptor endpoint=1 profile=260 device_type=7 (0x0007)
        # device_version=0
        # input_clusters=[0, 3, 4, 5, 514, 64599, 64637] output_clusters=[25, 1024, 1066]>
        MODELS_INFO: [(IKEA, "STARKVIND Air purifier")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,           #0
                    Identify.cluster_id,        #3 
                    Groups.cluster_id,          #4
                    Scenes.cluster_id,          #5
                    Fan.cluster_id,             #514    0x0202
                    WWAH_CLUSTER_ID,            #64599  0xFC57
                    IKEA_CLUSTER_UNKNOWN_ID,    #64637  0xFC7D
                ],
                 OUTPUT_CLUSTERS: [
                    Ota.cluster_id,                     #25      0x0019
                    IlluminanceMeasurement.cluster_id,  #1024    0x0400
                    PM25.cluster_id,                             #1066    0x042A PM2.5 Measurement Cluster
                ],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # device_version=0
            # input_clusters=[33] output_clusters=[33]>
            242: {
                PROFILE_ID: 0xA1E0, # 41440 (dec)
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [
                    GreenPowerProxy.cluster_id, #0x0021 = GreenPowerProxy.cluster_id?
                ], 
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,           #0
                    Identify.cluster_id,        #3 
                    Groups.cluster_id,          #4
                    Scenes.cluster_id,          #5
                    Fan.cluster_id,             #514
                    WWAH_CLUSTER_ID,            #64599  0xFC57
                    IKEA_CLUSTER_UNKNOWN_ID,    #64637  0xFC7D
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,                     #25      0x0019
                    IlluminanceMeasurement.cluster_id,  #1024    0x0400
                    PM25.cluster_id,                             #1066    0x042A PM2.5 Measurement Cluster
                ],
            }
        }
    }
    

Step 1: The quirk is recognised :)

@arnemauer
Copy link
Contributor

arnemauer commented Jun 1, 2022

class PM25(_ConcentrationMixin, Cluster) is already in the zigpy code. https://github.com/zigpy/zigpy/pull/683/files#diff-ec4c5179b55bb221fdcf288218a3e4ee5ccabcbefa8497468b6231429f669174

But this type of sensor is not available in HA. I created a pull request to add the Particulate Matter 2.5 sensor to ZHA component. :)

=============================

Here is a scan of all attribute values of the Starkvind:
STARKVIND Air purifier_IKEA of Sweden_fffea8b421_scan_results.txt

@arnemauer
Copy link
Contributor

arnemauer commented Jun 7, 2022

I have some things working :)

  • 2.5 PM value is getting reported
  • configure reporting for filter_run_time, Air Quality (PM2.5 in µg/m³), Target Mode and Current Mode
  • switch state of child lock and disable leds is showed in HA.

But i have one problem to solve....
There are two switches, lock control (childlock) and disable leds. I used two OnOff clusters in a seperate endpoint to make these switches available in homeassistant. Just like the information in the readme (https://github.com/zigpy/zha-device-handlers). But what about sending the switch event to the Starkvind? Homeassistant sends a switch on commando to 0x0006 (onoff cluster). How can i intercept that command or change de clusterID before zigpy sends the command to starkvind?

I think we have three possible options. Make a custom cluster and let it be recognised as an onoff cluster. Or make a onoff cluster, always return a possive result, and push the command to the ikea manuf cluster. Or make an onoff cluster and intercept the command and change the cluster ID before sending the command. Whats the best option, if possible at all....

@MattWestb can you shine your light on this?




**Click to see the latest code!**
"""Device handler for IKEA of Sweden STARKVIND Air purifier."""
import logging

from zigpy.profiles import zha
from zigpy.quirks import CustomCluster, CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import (
    Basic,
    Groups,
    Identify,
    Ota,
    PollControl,
    Scenes,
    GreenPowerProxy,
    Time,
    OnOff,
    AnalogInput,
)
from zigpy.zcl.clusters.hvac import Fan
from zhaquirks.ikea import IKEA
from zigpy.zcl.clusters.measurement import (
    IlluminanceMeasurement,
    PM25,
)
from zhaquirks import Bus

from zhaquirks.const import (
    CLUSTER_ID,
    DEVICE_TYPE,
    ENDPOINT_ID,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

from typing import Any, List, Optional, Union
from zigpy.zcl import foundation

#IKEA_CLUSTER_ID = 0xFC7C  # decimal = 64636

WWAH_CLUSTER_ID = 0xFC57  # decimal = 64599
IKEA_CLUSTER_UNKNOWN_ID = 0xFC7D

_LOGGER = logging.getLogger(__name__)


class manuSpecificIkeaAirPurifier(CustomCluster):
    """Ikea Manufacturer Specific AirPurifier."""

    name: str = "Ikea Manufacturer Specific AirPurifier"
    cluster_id: t.uint16_t = 0xFC7D         #64637  0xFC7D control air purifier with manufacturer-specific attributes 
    ep_attribute: str = "ikea_manufacturer"

    #attributes = TuyaMCUCluster.attributes.copy()
    attributes = {
            0x0000: ("filter_run_time", t.uint32_t, True),
            0x0001: ("replace_filter", t.Bool, True),
            0x0002: ("filter_life_time", t.uint32_t, True),
            0x0003: ("disable_led", t.Bool, True), 
            0x0004: ("air_quality_25pm", t.uint16_t, True), 
            0x0005: ("child_lock", t.Bool, True), 
            0x0006: ("fan_target_mode", t.uint8_t, True), 
            0x0007: ("fan_current_mode", t.uint8_t, True), 
            0x0008: ("device_run_time", t.uint32_t, True), 
        }
    
    async def configure_reporting(
        self,
        attribute,
        min_interval,
        max_interval,
        reportable_change,
        manufacturer=None,
    ):
        """Configure reporting."""
        result = await super().configure_reporting_multiple(
         {
         0x0000: (600, 600, 0),   # filter_run_time
         #0x0001: (0, 3600, 1),   # replace_filter
         #0x0002: (600, 600, 1),   # filter_life_time
         #0x0003: (0, 3600, 1),   # disable_led
         0x0004: (60, 3600, 1),   # Air Quality (PM2.5 in µg/m³)
         #0x0005: (0, 3600, 1),   # child_lock
         0x0006: (0, 3600, 1),   # Target Mode
         0x0007: (0, 3600, 1),   # Current Mode
         #0x0008: (600, 600, 1),   # device_run_time
         
         }, 
         manufacturer=0x117C)
        return result



    def __init__(self, *args, **kwargs):
        """Init."""
        self._current_state = {}
        super().__init__(*args, **kwargs)
        self.endpoint.device.child_lock_bus.add_listener(self)



    def _update_attribute(self, attrid, value):
        super()._update_attribute(attrid, value)
        if attrid == 0x0003:
            self.endpoint.device.disable_led_bus.listener_event("update_state", value)


        if attrid == 0x0004:
            if value is not 0xFFFF:  #pm 2.5 value if device is off
                if value is not None and value >= 0:
                    self.endpoint.device.pm25_bus.listener_event("update_state", value)
        

        if attrid == 0x0005:
            self.endpoint.device.child_lock_bus.listener_event("update_state", value)

#    def update_child_lock(self, attrid, value):




class childLockCluster(CustomCluster, OnOff):
    """ xxxx """

    cluster_id = OnOff.cluster_id

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.child_lock_bus.add_listener(self)
        
#    def _update_attribute(self, attrid, value):

    def update_state(self, value):
        """childlock reported."""
        self._update_attribute(0x0000, value)
        
        
        
                    #self.endpoint.device.child_lock_bus.listener_event("update_state", value)


    def command(
        self,
        command_id: Union[foundation.GeneralCommand, int, t.uint8_t],
        *args,
        manufacturer: Optional[Union[int, t.uint16_t]] = None,
        expect_reply: bool = True,
        tsn: Optional[Union[int, t.uint8_t]] = None,
    ):
        """Command handler."""
        src_ep = 1
        dst_ep = self.endpoint.endpoint_id
        device = self.endpoint.device
        if tsn is None:
            tsn = self._endpoint.device.application.get_sequence()
        return device.request(
            # device,
            zha.PROFILE_ID,
            0xFC7D,  #OnOff.cluster_id,
            src_ep,
            dst_ep,
            tsn,
            bytes([src_ep, tsn, command_id]),
            expect_reply=expect_reply,
        )
        
        
        

#    async def command(
#        self,
#        command_id: Union[foundation.GeneralCommand, int, t.uint8_t],
#        *args,
#        manufacturer: Optional[Union[int, t.uint16_t]] = None,
#        expect_reply: bool = True,
#        tsn: Optional[Union[int, t.uint8_t]] = None,
#    ):
#        """Override the default Cluster command."""
#        """Command handler."""
#        src_ep = 1
#        dst_ep = self.endpoint.endpoint_id
#        device = self.endpoint.device
#        if tsn is None:
#            tsn = self._endpoint.device.application.get_sequence()
#        return device.request(
#            # device,
#            zha.PROFILE_ID,
#            0xFC7D,  #OnOff.cluster_id,
#            src_ep,
#            dst_ep,
#            tsn,
#            bytes([src_ep, tsn, command_id]),
#            expect_reply=expect_reply,
#        )




class disableLedCluster(CustomCluster, OnOff):
    """ disableLedCluster """

    cluster_id = OnOff.cluster_id

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.disable_led_bus.add_listener(self)
        

    def update_state(self, value):
        """childlock reported."""
        self._update_attribute(0x0000, value)




class PM25Cluster(CustomCluster, PM25):
    """Analog input cluster, only used to relay power consumption information to ElectricalMeasurementCluster."""
    cluster_id = PM25.cluster_id

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.pm25_bus.add_listener(self)
        

    def update_state(self, value):
        """25pm reported."""
        self._update_attribute(0x0000, value)
    

    
    
    
class IkeaSTARKVIND(CustomDevice):
    """STARKVIND Air purifier by IKEA of Sweden."""

    def __init__(self, *args, **kwargs):
        """Init."""
        self.pm25_bus = Bus()
        self.child_lock_bus = Bus()
        self.disable_led_bus = Bus()


        super().__init__(*args, **kwargs)


    signature = {
        # <SimpleDescriptor endpoint=1 profile=260 device_type=7 (0x0007)
        # device_version=0
        # input_clusters=[0, 3, 4, 5, 514, 64599, 64637] output_clusters=[25, 1024, 1066]>
        MODELS_INFO: [(IKEA, "STARKVIND Air purifier")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,           #0
                    Identify.cluster_id,        #3 
                    Groups.cluster_id,          #4
                    Scenes.cluster_id,          #5
                    Fan.cluster_id,             #514    0x0202
                    WWAH_CLUSTER_ID,            #64599  0xFC57
                    manuSpecificIkeaAirPurifier.cluster_id,    #64637  0xFC7D
                ],
                 OUTPUT_CLUSTERS: [
                    Ota.cluster_id,                     #25      0x0019
                    IlluminanceMeasurement.cluster_id,  #1024    0x0400
                    PM25.cluster_id,                             #1066    0x042A PM2.5 Measurement Cluster
                ],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # device_version=0
            # input_clusters=[33] output_clusters=[33]>
            242: {
                PROFILE_ID: 0xA1E0, # 41440 (dec)
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [
                    GreenPowerProxy.cluster_id, #0x0021 = GreenPowerProxy.cluster_id?
                ], 
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,           #0
                    Identify.cluster_id,        #3 
                    Groups.cluster_id,          #4
                    Scenes.cluster_id,          #5
                    Fan.cluster_id,             #514
                    WWAH_CLUSTER_ID,            #64599  0xFC57
                    manuSpecificIkeaAirPurifier,    #64637  0xFC7D control air purifier with manufacturer-specific attributes 
                    PM25Cluster,                             #1066    0x042A PM2.5 Measurement Cluster
                    #PM25,                             #1066    0x042A PM2.5 Measurement Cluster
                    #childLockCluster,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,                     #25      0x0019
                    IlluminanceMeasurement.cluster_id,  #1024    0x0400
                ],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
                INPUT_CLUSTERS: [
                    disableLedCluster,
                ],
                OUTPUT_CLUSTERS: [ ],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # device_version=0
            # input_clusters=[33] output_clusters=[33]>
            242: {
                PROFILE_ID: 0xA1E0, # 41440 (dec)
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [
                    GreenPowerProxy.cluster_id, #0x0021 = GreenPowerProxy.cluster_id?
                ], 
            },
        },
    }

@arnemauer
Copy link
Contributor

image

Almost everything is working :)
HA has only 3 fan speeds and the starkvind has 5 speeds + auto + off.

@mguaylam
Copy link
Contributor

mguaylam commented Jun 9, 2022

Interestingly there seems to have intermediate values possible on the fan cluster even if the physical dial only increments by 5 steps :

I wonder if something like this is possible :
0 : off
1 : auto
10 : 20 %
15 : 30 %
20 : 40 %
25 : 50 %
30 : 60 %
35 : 70 %
40 : 80 %
45 : 90 %
50 : 100%

@arnemauer
Copy link
Contributor

The fan cluster has only speed 1, 2 and 3 + ON + AUTO + Smart.
Ikea uses an manufacturer cluster. That makes is very difficult to make an sensor in Homeassistant that supports all modes. I tried to ajust the Fan cluster but that is very difficult.

An easier option is to add a select list to the "configuration" option. Is dat usable?

image

@arnemauer
Copy link
Contributor

arnemauer commented Jun 11, 2022

Maybe someone can point me in the right direction...
I want to write an value to the an attribute in the ikea cluster. How do i do this?
I tried to use _write_attributes but nothing happens....

        new_fan_mode = {"fan_mode": value}
        attrs = self._write_attr_records(new_fan_mode)
        result = self._write_attributes(attrs, IKEA_MANUF_ID)

Zigpy.zcl is sending a request, but nothing happens after that.

 20:02:43 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=True, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), manufacturer=4476, tsn=146, command_id=<GeneralCommand.Write_Attributes: 2>, *is_reply=False)
2022-06-10 20:02:43 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Sending request: Write_Attributes(attributes=[Attribute(attrid=0x0006, value=TypeValue(type=uint8_t, value=30))])
2022-06-10 

When i send the same write attribute command with ZHA toolkit i see the same "Sending request:". After that zigpy_deconz.zigbee.application is sending the request over zigbee.

2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=True, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), manufacturer=4476, tsn=219, command_id=<GeneralCommand.Write_Attributes: 2>, *is_reply=False)
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Sending request: Write_Attributes(attributes=[Attribute(attrid=0x0006, value=TypeValue(type=uint8_t, value=1))])
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.zigbee.application] Sending Zigbee request with tsn 219 under 220 request id, data: b'047c11db0206002001'
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_request (24, 220, <DeconzSendDataFlags.NONE: 0>, <DeconzAddressEndpoint address_mode=2 address=0x0B53 endpoint=1>, 260, 64637, 1, b'\x04|\x11\xdb\x02\x06\x00 \x01', <DeconzTransmitOptions.USE_NWK_KEY_SECURITY: 2>, 0)
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] APS data request response: [2, <DeviceState.APSDE_DATA_REQUEST_SLOTS_AVAILABLE|2: 34>, 220]
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [<DeviceState.128|APSDE_DATA_REQUEST_SLOTS_AVAILABLE|APSDE_DATA_CONFIRM|2: 166>, 0]
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_confirm (0,)
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] APS data confirm response for request with id 220: 00
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Request id: 0xdc 'aps_data_confirm' for <DeconzAddressEndpoint address_mode=ADDRESS_MODE.NWK address=0x0b53 endpoint=1>, status: 0x00
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [<DeviceState.128|APSDE_DATA_REQUEST_SLOTS_AVAILABLE|APSDE_DATA_INDICATION|2: 170>, 0]
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, 1)
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [29, <DeviceState.APSDE_DATA_REQUEST_SLOTS_AVAILABLE|APSDE_DATA_INDICATION|2: 42>, <DeconzAddress address_mode=ADDRESS_MODE.NWK address=0x0000>, 1, <DeconzAddress address_mode=ADDRESS_MODE.NWK address=0x0b53>, 1, 260, 64637, b'\x1c|\x11\xdb\x04\x00', 0, 175, 255, 29, 225, 40, 0, -55]
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Received ZCL frame: b'\x1c|\x11\xdb\x04\x00'
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=True, is_reply=1, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), manufacturer=4476, tsn=219, command_id=4, *is_reply=True)
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Decoded ZCL frame: manuSpecificIkeaAirPurifier:Write_Attributes_rsp(status_records=[WriteAttributesStatusRecord(status=<Status.SUCCESS: 0>)])
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from <DeconzAddress address_mode=ADDRESS_MODE.NWK address=0x0b53>, ep: 1, profile: 0x0104, cluster_id: 0xfc7d, data: b'1c7c11db0400'
2022-06-10 20:16:34 DEBUG (MainThread) [custom_components.zha_toolkit.zcl_attr] Write attr status: Write_Attributes_rsp(status_records=[WriteAttributesStatusRecord(status=<Status.SUCCESS: 0>)])
2022-06-10 20:16:34 DEBUG (MainThread) [custom_components.zha_toolkit.zcl_attr] Write success: True
2022-06-10 20:16:34 DEBUG (MainThread) [custom_components.zha_toolkit.zcl_attr] Request attr read [6]
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=True, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), manufacturer=4476, tsn=221, command_id=<GeneralCommand.Read_Attributes: 0>, *is_reply=False)
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy.zcl] [0x0B53:1:0xfc7d] Sending request: Read_Attributes(attribute_ids=[6])
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.zigbee.application] Sending Zigbee request with tsn 221 under 222 request id, data: b'047c11dd000600'
2022-06-10 20:16:34 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, 1)

How can we write attributes from a quirk?

@MattWestb
Copy link
Contributor

MattWestb commented Jun 11, 2022

We is reading attribute for casting "tuya magic spell" and was also testing one version with writing one attribute but it was needed. Look in this class for see how its being done:

class TuyaSmartRemote004FDMS(CustomDevice):
.

Ar you sure the attribute is one manufacture attribute then you have tagging it with IKEA as manufacture ?
Normally IKEA is using real ZCL commands and attribute and dont need tagging them with manufacture.

@arnemauer
Copy link
Contributor

arnemauer commented Jun 11, 2022

@MattWestb Thanks! I see we can write attribute from CustomDevice class. But is it also possible from Customcluster? Yeah, fixed it! 👍

Tagging it with IKEA as manufacturer isn't necessary. Just tried different things to get it working :)

@arnemauer
Copy link
Contributor

Screenshot_20220611-220854_Home Assistant

Almost done! 👍

@MattWestb
Copy link
Contributor

Looks great !!
Only need driving to IKEA and baying one or 2 but the have being expensive then ´was 100 € but now they is 130€ :-((

Great work done !!!

@javontathomas
Copy link

Hi! Is there anything special I have to do to get this functionality to show up? I paired the air purifier and only get an on/off function…

@TheJulianJES
Copy link
Collaborator

@javontathomas Are you running the latest Home Assistant 2022.7.0 beta?

@javontathomas
Copy link

@javontathomas Are you running the latest Home Assistant 2022.7.0 beta?

Ahh, didn’t realize I had to be on beta. Thanks! Will switch over.

@joerimul
Copy link

joerimul commented Jul 6, 2022

Is there any chance you can add the IKEA Starkvind table? It is basically the same device, but built into a table.
Home Assistant 2022.7 only recognizes the fan.

{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, manufacturer_code=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0007",
      "in_clusters": [
        "0x0000",
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0202",
        "0xfc57",
        "0xfc7d"
      ],
      "out_clusters": [
        "0x0019",
        "0x0400",
        "0x042a"
      ]
    },
    "242": {
      "profile_id": 41440,
      "device_type": "0x0061",
      "in_clusters": [],
      "out_clusters": [
        "0x0021"
      ]
    }
  },
  "manufacturer": "IKEA of Sweden",
  "model": "STARKVIND Air purifier table",
  "class": "zigpy.device.Device"
}
New device 0x14cf (cc:86:ec:ff:fe:7d:ef:c8) joined the network
[0x14cf] Scheduling initialization
Received frame on uninitialized device <Device model=None manuf=None nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=False> from ep 0 to ep 0, cluster 19: b'\x81\xcf\x14\xc8\xef}\xfe\xff\xec\x86\xcc\x8e'
[0x14cf:zdo] ZDO request ZDOCmd.Device_annce: [0x14CF, cc:86:ec:ff:fe:7d:ef:c8, 142]
Tries remaining: 3
[0x14cf] Requesting 'Node Descriptor'
Tries remaining: 2
[0x14cf] Extending timeout for 0xd4 request
Received frame on uninitialized device <Device model=None manuf=None nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=False> from ep 0 to ep 0, cluster 32770: b'\xd4\x00\xcf\x14\x01@\x8e|\x11RR\x00\x00,R\x00\x00'
[0x14cf] Got Node Descriptor: NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, manufacturer_code=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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)
[0x14cf] Discovering endpoints
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=False> from ep 0 to ep 0, cluster 32773: b'\xd6\x00\xcf\x14\x02\x01\xf2'
[0x14cf] Discovered endpoints: [1, 242]
[0x14cf] Initializing endpoints [<Endpoint id=1 in=[] out=[] status=<Status.NEW: 0>>, <Endpoint id=242 in=[] out=[] status=<Status.NEW: 0>>]
[0x14cf:1] Discovering endpoint information
Tries remaining: 3
[0x1B2F](TRADFRI bulb E27 WS opal 1000lm): Device seen - marking the device available and resetting counter
[0x1B2F](TRADFRI bulb E27 WS opal 1000lm): Update device availability -  device available: True - new availability: True - changed: False
Received frame on uninitialized device <Device model=None manuf=None nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=False> from ep 0 to ep 0, cluster 32772: b'\xd8\x00\xcf\x14\x1c\x01\x04\x01\x07\x00\x01\x07\x00\x00\x03\x00\x04\x00\x05\x00\x02\x02W\xfc}\xfc\x03\x19\x00\x00\x04*\x04'
[0x14cf:1] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=7, device_version=1, input_clusters=[0, 3, 4, 5, 514, 64599, 64637], output_clusters=[25, 1024, 1066])
[0x14cf:242] Discovering endpoint information
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=False> from ep 0 to ep 0, cluster 32772: b'\xda\x00\xcf\x14\n\xf2\xe0\xa1a\x00\x00\x00\x01!\x00'
[0x14cf:242] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=242, profile=41440, device_type=97, device_version=0, input_clusters=[], output_clusters=[33])
[0x14CF:1:0x0000] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=False, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=220, command_id=<GeneralCommand.Read_Attributes: 0>, *is_reply=False)
[0x14CF:1:0x0000] Sending request: Read_Attributes(attribute_ids=[4, 5])
[0x14CF:1:0x0000] Received ZCL frame: b'\x18\xdc\x01\x04\x00\x00B\x0eIKEA of Sweden\x05\x00\x00B\x1cSTARKVIND Air purifier table'
[0x14CF:1:0x0000] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, is_reply=1, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=220, command_id=1, *is_reply=True)
[0x14CF:1:0x0000] Decoded ZCL frame: Basic:Read_Attributes_rsp(status_records=[ReadAttributeRecord(attrid=0x0004, status=<Status.SUCCESS: 0>, value=TypeValue(type=CharacterString, value='IKEA of Sweden')), ReadAttributeRecord(attrid=0x0005, status=<Status.SUCCESS: 0>, value=TypeValue(type=CharacterString, value='STARKVIND Air purifier table'))])
[0x14cf] Read model 'STARKVIND Air purifier table' and manufacturer 'IKEA of Sweden' from <Endpoint id=1 in=[basic:0x0000, identify:0x0003, groups:0x0004, scenes:0x0005, fan:0x0202, manufacturer_specific:0xFC57, manufacturer_specific:0xFC7D] out=[ota:0x0019, illuminance:0x0400, pm25:0x042A] status=<Status.ZDO_INIT: 1>>
[0x14cf] Discovered basic device information for <Device model='STARKVIND Air purifier table' manuf='IKEA of Sweden' nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=True>
Device is initialized <Device model='STARKVIND Air purifier table' manuf='IKEA of Sweden' nwk=0x14CF ieee=cc:86:ec:ff:fe:7d:ef:c8 is_initialized=True>
Checking quirks for IKEA of Sweden STARKVIND Air purifier table (cc:86:ec:ff:fe:7d:ef:c8)
Considering <class 'zhaquirks.ikea.tradfriplug.TradfriPlug'>
Fail because endpoint list mismatch: {1, 2, 242} {1, 242}
Considering <class 'zhaquirks.xbee.xbee_io.XBeeSensor'>
Fail because endpoint list mismatch: {232, 230} {1, 242}
Considering <class 'zhaquirks.xbee.xbee3_io.XBee3Sensor'>
Fail because endpoint list mismatch: {232, 230} {1, 242}
Considering <class 'zhaquirks.smartthings.tag_v4.SmartThingsTagV4'>
Fail because endpoint list mismatch: {1} {1, 242}
Considering <class 'zhaquirks.smartthings.multi.SmartthingsMultiPurposeSensor'>
Fail because endpoint list mismatch: {1} {1, 242}
Considering <class 'zhaquirks.netvox.z308e3ed.Z308E3ED'>
Fail because endpoint list mismatch: {1} {1, 242}
Considering <class 'zhaquirks.gledopto.soposhgu10.SoposhGU10'>
Fail because endpoint list mismatch: {11, 13} {1, 242}
'button' component -> 'ZHAIdentifyButton' using ['identify']
'sensor' component -> 'RSSISensor' using ['basic']
'sensor' component -> 'LQISensor' using ['basic']
device - 0x14CF:cc:86:ec:ff:fe:7d:ef:c8 entering async_device_initialized - is_new_join: True
device - 0x14CF:cc:86:ec:ff:fe:7d:ef:c8 has joined the ZHA zigbee network
[0x14CF](STARKVIND Air purifier table): started configuration
[0x14CF:ZDO](STARKVIND Air purifier table): 'async_configure' stage succeeded
[0x14CF:1:0x0000]: Configuring cluster attribute reporting
[0x14CF:1:0x0000]: finished channel configuration
[0x14CF:1:0x0003]: Configuring cluster attribute reporting
[0x14CF:1:0x0003]: finished channel configuration
[0x14CF:1:0xfc7d]: Performing cluster binding
[0x14CF:1:0x0202]: Performing cluster binding
[0x14CF:1:0x0019]: finished channel configuration
Error handling '_save_attribute' event with (cc:86:ec:ff:fe:7d:ef:c8, 1, 0, 4, 'IKEA of Sweden') params: FOREIGN KEY constraint failed
Error handling '_save_attribute' event with (cc:86:ec:ff:fe:7d:ef:c8, 1, 0, 5, 'STARKVIND Air purifier table') params: FOREIGN KEY constraint failed
[0x14CF:1:0xfc7d]: bound 'manufacturer_specific' cluster: Status.SUCCESS
[0x14CF:1:0xfc7d]: Configuring cluster attribute reporting
[0x14CF:1:0x0202]: bound 'fan' cluster: Status.SUCCESS
[0x14CF:1:0x0202]: Configuring cluster attribute reporting
[0x14CF:1:0x0202] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=False, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=226, command_id=<GeneralCommand.Configure_Reporting: 6>, *is_reply=False)
[0x14CF:1:0x0202] Sending request: Configure_Reporting(config_records=[AttributeReportingConfig(direction=0, attrid=0x0000, datatype=48, min_interval=5, max_interval=900, reportable_change=1)])
[0x14CF:1:0x0202] Received ZCL frame: b'\x08\xe2\x07\x00'
[0x14CF:1:0x0202] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, is_reply=1, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=226, command_id=7, *is_reply=True)
[0x14CF:1:0x0202] Decoded ZCL frame: Fan:Configure_Reporting_rsp(status_records=[ConfigureReportingResponseRecord(status=0)])
[0x14CF:1:0x0202]: Successfully configured reporting for '{'fan_mode': (5, 900, 1)}' on 'fan' cluster: [ConfigureReportingResponseRecord(status=0)]
[0x14CF:1:0x0202]: finished channel configuration
[0x14CF:1:0x0000]: 'async_configure' stage succeeded
[0x14CF:1:0x0003]: 'async_configure' stage succeeded
[0x14CF:1:0xfc7d]: 'async_configure' stage failed: Unknown attribute 'filter_run_time' of <zigpy.zcl.clusters.manufacturer_specific.ManufacturerSpecificCluster object at 0x7fd73aa46800> cluster
[0x14CF:1:0x0202]: 'async_configure' stage succeeded
[0x14CF:1:0x0019]: 'async_configure' stage succeeded
[0x14CF](STARKVIND Air purifier table): completed configuration
[0x14CF](STARKVIND Air purifier table): stored in registry: ZhaDeviceEntry(name='IKEA of Sweden STARKVIND Air purifier table', ieee='cc:86:ec:ff:fe:7d:ef:c8', last_seen=1657146298.103479)
[0x14CF:1:0x0003] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=False, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=True, *is_general=False), tsn=228, command_id=64, *is_reply=False)
[0x14CF:1:0x0003] Sending request: trigger_effect(effect_id=<EffectIdentifier.Okay: 2>, effect_variant=<EffectVariant.Default: 0>)
[0x14CF:1:0x0003] Received ZCL frame: b'\x08\xe4\x0b@\x00'
[0x14CF:1:0x0003] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, is_reply=1, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=228, command_id=11, *is_reply=True)
[0x14CF:1:0x0003] Decoded ZCL frame: Identify:Default_Response(command_id=64, status=<Status.SUCCESS: 0>)
[0x14CF:1:0x0003]: executed 'trigger_effect' command with args: '(2, 0)' kwargs: '{}' result: Default_Response(command_id=64, status=<Status.SUCCESS: 0>)
[0x14CF](STARKVIND Air purifier table): started initialization
[0x14CF:ZDO](STARKVIND Air purifier table): 'async_initialize' stage succeeded
[0x14CF:1:0x0000]: initializing channel: from_cache: False
[0x14CF:1:0x0000]: finished channel initialization
[0x14CF:1:0x0003]: initializing channel: from_cache: False
[0x14CF:1:0x0003]: finished channel initialization
[0x14CF:1:0xfc7d]: initializing channel: from_cache: False
[0x14CF:1:0xfc7d]: initializing uncached channel attributes: ['filter_run_time', 'replace_filter', 'filter_life_time', 'disable_led', 'air_quality_25pm', 'child_lock', 'fan_mode', 'fan_speed', 'device_run_time'] - from cache[False]
[0x14CF:1:0xfc7d]: Reading attributes in chunks: ['filter_run_time', 'replace_filter', 'filter_life_time', 'disable_led', 'air_quality_25pm']
[0x14CF:1:0x0202]: initializing channel: from_cache: False
[0x14CF:1:0x0202]: initializing cached channel attributes: ['fan_mode_sequence']
[0x14CF:1:0x0202]: Reading attributes in chunks: ['fan_mode_sequence']
[0x14CF:1:0x0202] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=False, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=230, command_id=<GeneralCommand.Read_Attributes: 0>, *is_reply=False)
[0x14CF:1:0x0202] Sending request: Read_Attributes(attribute_ids=[1])
[0x14CF:1:0x0019]: initializing channel: from_cache: False
[0x14CF:1:0x0019]: finished channel initialization
[0x14CF:1:0x0202] Received ZCL frame: b'\x18\xe6\x01\x01\x00\x000\x02'
[0x14CF:1:0x0202] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, is_reply=1, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=230, command_id=1, *is_reply=True)
[0x14CF:1:0x0202] Decoded ZCL frame: Fan:Read_Attributes_rsp(status_records=[ReadAttributeRecord(attrid=0x0001, status=<Status.SUCCESS: 0>, value=TypeValue(type=enum8, value=<enum8.undefined_0x02: 2>))])
[0x14CF:1:0x0202]: Attribute report 'Fan Control'[fan_mode_sequence] = FanModeSequence.Low_Med_High_Auto
[0x14CF:1:0x0202]: initializing uncached channel attributes: ['fan_mode'] - from cache[False]
[0x14CF:1:0x0202]: Reading attributes in chunks: ['fan_mode']
[0x14CF:1:0x0202] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=False, is_reply=0, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=232, command_id=<GeneralCommand.Read_Attributes: 0>, *is_reply=False)
[0x14CF:1:0x0202] Sending request: Read_Attributes(attribute_ids=[0])
[0x14CF:1:0x0202] Received ZCL frame: b'\x18\xe8\x01\x00\x00\x000\x00'
[0x14CF:1:0x0202] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, is_reply=1, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=232, command_id=1, *is_reply=True)
[0x14CF:1:0x0202] Decoded ZCL frame: Fan:Read_Attributes_rsp(status_records=[ReadAttributeRecord(attrid=0x0000, status=<Status.SUCCESS: 0>, value=TypeValue(type=enum8, value=<enum8.undefined_0x00: 0>))])
[0x14CF:1:0x0202]: Attribute report 'Fan Control'[fan_mode] = FanMode.Off
[0x14CF:1:0x0202]: finished channel initialization
[0x14CF:1:0x0000]: 'async_initialize' stage succeeded
[0x14CF:1:0x0003]: 'async_initialize' stage succeeded
[0x14CF:1:0xfc7d]: 'async_initialize' stage failed: 'filter_run_time'
[0x14CF:1:0x0202]: 'async_initialize' stage succeeded
[0x14CF:1:0x0019]: 'async_initialize' stage succeeded
[0x14CF](STARKVIND Air purifier table): power source: Mains
[0x14CF](STARKVIND Air purifier table): completed initialization

@arnemauer
Copy link
Contributor

I have a pull request ready to support this model :)

@joerimul
Copy link

joerimul commented Jul 7, 2022

@arnemauer Awesome, thanks!

@arnemauer
Copy link
Contributor

@kcleong This issue can be closed! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests