Skip to content

Commit

Permalink
Refactor modules
Browse files Browse the repository at this point in the history
- Add CH2AX/SWITCH/1 signature
  • Loading branch information
axellebot committed Aug 31, 2022
1 parent e01b590 commit 6b90d19
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 187 deletions.
54 changes: 50 additions & 4 deletions tests/test_schneiderelectric.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from zigpy.zcl import foundation

import zhaquirks.schneiderelectric.shutters
import zhaquirks.schneiderelectric.switches
import zhaquirks.schneiderelectric.devices.shutters
import zhaquirks.schneiderelectric.devices.switches

zhaquirks.setup()

Expand Down Expand Up @@ -121,7 +121,53 @@ async def test_fls_air_link_4_signature(assert_signature_matches_quirk):
"class": "zigpy.device.Device",
}
assert_signature_matches_quirk(
zhaquirks.schneiderelectric.switches.FLSAirlink4, signature
zhaquirks.schneiderelectric.devices.switches.FLSAirlink4, signature
)


async def test_ch2ax_switch_1_signature(assert_signature_matches_quirk):
signature = {
"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=4190, 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": "0x0100",
"in_clusters": [
"0x0000",
"0x0003",
"0x0004",
"0x0005",
"0x0006",
"0x0b05",
],
"out_clusters": ["0x0019"],
},
"21": {
"profile_id": 260,
"device_type": "0x0104",
"in_clusters": ["0x0000", "0x0003", "0x0b05", "0xff17"],
"out_clusters": [
"0x0003",
"0x0004",
"0x0005",
"0x0006",
"0x0008",
"0x0102",
],
},
"242": {
"profile_id": 41440,
"device_type": "0x0061",
"in_clusters": [],
"out_clusters": ["0x0021"],
},
},
"manufacturer": "Schneider Electric",
"model": "CH2AX/SWITCH/1",
"class": "zigpy.device.Device",
}
assert_signature_matches_quirk(
zhaquirks.schneiderelectric.devices.switches.CHxAXSwitch1, signature
)


Expand Down Expand Up @@ -167,5 +213,5 @@ async def test_ch10ax_switch_1_signature(assert_signature_matches_quirk):
"class": "zigpy.device.Device",
}
assert_signature_matches_quirk(
zhaquirks.schneiderelectric.switches.CH10AXSwitch1, signature
zhaquirks.schneiderelectric.devices.switches.CHxAXSwitch1, signature
)
18 changes: 13 additions & 5 deletions zhaquirks/schneiderelectric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
- [Helpers](#helpers)
- [Devices](#devices)
- [Shutter](#shutter)
- [NHPB/SHUTTER/1 ](#nhpbshutter1-)
- [NHPB/SHUTTER/1 ](#nhpbshutter1-)
- [PUCK/SHUTTER/1 ❌](#puckshutter1-)
- [1GANG/SHUTTER/1 ❔](#1gangshutter1-)
- [Switch](#switch)
- [CH2AX/SWITCH/1 ](#ch2axswitch1-)
- [CH2AX/SWITCH/1 ](#ch2axswitch1-)
- [CH10AX/SWITCH/1 ❌](#ch10axswitch1-)
- [FLS/AIRLINK/4 ❌](#flsairlink4-)
- [FLS/SYSTEM-M/4 ❔](#flssystem-m4-)
Expand Down Expand Up @@ -53,7 +53,7 @@ The purpose of this file is to list all Zigbee capable device from Schneider Ele

All devices are listed alphabetically based on the Zigbee `model (0x0005)` attribute from `Basic (0x0000)` cluster.

All quirk infos should remain in basecode.
All quirk infos should remain in basecode.

Status :

Expand All @@ -79,7 +79,7 @@ Sources :

Lift percentage is reversed

#### NHPB/SHUTTER/1
#### NHPB/SHUTTER/1

<details>
<summary>Device signature</summary>
Expand Down Expand Up @@ -2015,7 +2015,7 @@ From [Jeedom community](https://community.jeedom.com/t/plugin-zigbee-beta-blabla

### Switch

#### CH2AX/SWITCH/1
#### CH2AX/SWITCH/1

<details>
<summary>Signature</summary>
Expand Down Expand Up @@ -4245,6 +4245,14 @@ From [Jeedom community](https://community.jeedom.com/t/plugin-zigbee-beta-blabla

#### FLS/AIRLINK/4 ❌

[zigbee2mqtt](https://www.zigbee2mqtt.io/devices/550D6001.html)

> Depending on the firmware version the device may support both 1-channel and 2-channel mode. In 1-channel mode both the upper and lower buttons works as the upper buttons. On some firmware versions (incl. those sold as Elko EKO07117) the device starts out in 1-channel mode and must be switched into 2-channel mode either by using the Elko / Wiser gateway or by holding down button 1 and 4 (upper left and lower right) for approx. 10 seconds - the led will flash red and the become green once successfull.
[zigbee-herdsman-converters](https://github.com/Koenkk/zigbee-herdsman-converters/blob/d1f00202a79a5cd4b6548dd1d15057895a3e6666/devices/schneider_electric.js#L292-L311)

> When in 2-gang operation mode, unit operates out of endpoints 21 and 22, otherwise just 21
<details>
<summary>Signature</summary>

Expand Down
170 changes: 0 additions & 170 deletions zhaquirks/schneiderelectric/__init__.py
Original file line number Diff line number Diff line change
@@ -1,177 +1,7 @@
"""Module for Schneider Electric devices quirks."""
import logging

from zigpy.quirks import CustomCluster
import zigpy.types as t
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import Basic, OnOff
from zigpy.zcl.clusters.homeautomation import Diagnostic
from zigpy.zcl.foundation import ZCLAttributeDef

_LOGGER = logging.getLogger(__name__)

SE_MANUF_NAME = "Schneider Electric"
SE_MANUF_ID = 0x4190

# Attribute IDs
ATTR_CURRENT_POSITION_LIFT_PERCENTAGE = 0x0008

# Command IDs
CMD_GO_TO_LIFT_PERCENTAGE = 0x0005


class SEManufCluster(CustomCluster):
"""Schneider Electric manufacturer specific cluster."""

name = "Schneider Electric Manufacturer Specicific"
ep_attribute = "schneider_electric_manufacturer"


class SEBasicCluster(SEManufCluster, Basic):

attributes: dict[int, ZCLAttributeDef] = Basic.attributes.copy()

attributes.update(
{
0xE001: (
"se_sw_build_id",
t.CharacterString,
), # value: "002.004.016 R
0xE002: (
"unknown_attribute_57346",
t.CharacterString,
), # value: "001.000.000"
0xE004: (
"unknown_attribute_57348",
t.CharacterString,
), # value: "213249FEFF5ECFD"
0xE007: ("unknown_attribute_57351", t.enum16),
0xE008: (
"se_device_type",
t.CharacterString,
), # value: "Wiser Light"
0xE009: (
"se_model",
t.CharacterString,
), # value: "NHPB/SHUTTER/1"
0xE00A: (
"se_realm",
t.CharacterString,
), # value: "Wiser Home"
0xE00B: (
"unknown_attribute_57355",
t.CharacterString,
),
}
)


class SEOnOff(SEManufCluster, OnOff):
attributes = OnOff.attributes.copy()
attributes.update(
{
0xE000: ("unknown_attribute_57344", t.uint16_t),
0xE001: ("unknown_attribute_57345", t.uint32_t),
0xE002: ("unknown_attribute_57346", t.bitmap8),
0xE003: ("unknown_attribute_57347", t.uint32_t),
}
)


class SEWindowCovering(SEManufCluster, WindowCovering):
"""Manufacturer Specific Cluster of cover device."""

attributes: dict[int, ZCLAttributeDef] = WindowCovering.attributes.copy()

attributes.update(
{
0xFFFD: ("unknown_attribute_65533", t.uint16_t),
0xE000: ("lift_duration", t.uint16_t),
0xE010: ("unknown_attribute_57360", t.bitmap8),
0xE012: ("unknown_attribute_57362", t.uint16_t),
0xE013: ("unknown_attribute_57363", t.bitmap8),
0xE014: ("unknown_attribute_57364", t.uint16_t),
0xE015: ("unknown_attribute_57365", t.uint16_t),
0xE016: ("unknown_attribute_57366", t.uint16_t),
0xE017: ("unknown_attribute_57367", t.uint8_t),
}
)

def _update_attribute(self, attrid, value):
if attrid == ATTR_CURRENT_POSITION_LIFT_PERCENTAGE:
# Invert the percentage value
value = 100 - value
super()._update_attribute(attrid, value)

async def command(
self, command_id, *args, manufacturer=None, expect_reply=True, tsn=None
):
"""Override default command to invert percent lift value."""
if command_id == CMD_GO_TO_LIFT_PERCENTAGE:
percent = args[0]
percent = 100 - percent
v = (percent,)
return await super().command(command_id, *v)
return await super().command(
command_id,
*args,
manufacturer=manufacturer,
expect_reply=expect_reply,
tsn=tsn
)


class SEDiagnostic(CustomCluster, Diagnostic):

attributes: dict[int, ZCLAttributeDef] = Diagnostic.attributes.copy()

# TODO: Check -> this attr don't seems to be manufacturer related (no "manf_id")
attributes.update(
{
0xFFFD: ("unknown_attribute_65533", t.uint16_t),
}
)


class SESpecificCluster(SEManufCluster):
cluster_id = 0xFF17

attributes = {
0x0000: ("unknown_attribute_0", t.enum8),
0x0001: ("unknown_attribute_1", t.enum8),
0x0010: ("unknown_attribute_16", t.uint8_t),
0x0011: ("unknown_attribute_17", t.uint16_t),
0x0020: ("unknown_attribute_32", t.uint8_t),
0x0021: ("unknown_attribute_33", t.uint16_t),
0xFFFD: ("unknown_attribute_65533", t.uint16_t),
}


class SEPiloteMode(int):
'''
Might be useful for :
- CCTFR6700 (manufacturerCode seems diff: 0x105e)
'''
Contactor=1
Pilot=3

class WiserDimmerMode(int):
'''
Might be useful for :
- PUCK/DIMMER/
- NHROTARY/DIMMER/1
'''
Auto = 0
RC = 1
RL = 2
RL_LED = 3


class SEDimmerMode():
'''
Might be useful for :
- LK Dimmer (manufacturerCode seems diff: 0x105e)
'''
RC = 1
RL = 2

0 comments on commit 6b90d19

Please sign in to comment.