Skip to content

Instantly share code, notes, and snippets.

@Bluscream
Last active November 7, 2023 13:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bluscream/b20a57f378461ac1f0bed640596d3432 to your computer and use it in GitHub Desktop.
Save Bluscream/b20a57f378461ac1f0bed640596d3432 to your computer and use it in GitHub Desktop.
zha_problems
{
"home_assistant": {
"installation_type": "Home Assistant OS",
"version": "2023.11.1",
"dev": false,
"hassio": true,
"virtualenv": false,
"python_version": "3.11.6",
"docker": true,
"arch": "aarch64",
"timezone": "Europe/Berlin",
"os_name": "Linux",
"os_version": "6.1.21-v8",
"supervisor": "2023.10.1",
"host_os": "Home Assistant OS 11.1",
"docker_version": "24.0.6",
"chassis": "embedded",
"run_as_root": true
},
"custom_components": {
"deutschebahn": {
"version": "2.0.4",
"requirements": [
"schiene==0.26"
]
},
"dht": {
"version": "2022.4.0",
"requirements": [
"adafruit-circuitpython-dht==3.7.0",
"RPi.GPIO==0.7.1",
"tenacity==8.0.1"
]
},
"smartir": {
"version": "1.17.8",
"requirements": [
"aiofiles>=0.6.0"
]
},
"rpi_gpio": {
"version": "2022.7.0",
"requirements": [
"RPi.GPIO==0.7.1"
]
},
"openrgb": {
"version": "2.3",
"requirements": [
"openrgb-python==0.2.10"
]
},
"openai_response": {
"version": "0.1.0",
"requirements": [
"openai==0.27.0"
]
},
"garbage_collection": {
"version": "4.10.2",
"requirements": [
"python-dateutil>=2.8.2"
]
},
"chatgpt": {
"version": "1.0.0",
"requirements": [
"requests>=2.28.2"
]
},
"overwolfstatus": {
"version": "1.0.1",
"requirements": []
},
"hass_agent": {
"version": "2022.11.9",
"requirements": []
},
"ytube_music_player": {
"version": "20230321.01",
"requirements": [
"ytmusicapi==0.25.0",
"pytube==10.5.1",
"integrationhelper==0.2.2"
]
},
"localtuya": {
"version": "5.2.1",
"requirements": []
},
"climate_template": {
"version": "0.6.1",
"requirements": []
},
"watchman": {
"version": "0.5.1",
"requirements": [
"prettytable==3.0.0"
]
},
"hacs": {
"version": "1.33.0",
"requirements": [
"aiogithubapi>=22.10.1"
]
}
},
"integration_manifest": {
"domain": "zha",
"name": "Zigbee Home Automation",
"after_dependencies": [
"onboarding",
"usb"
],
"codeowners": [
"@dmulcahey",
"@adminiuga",
"@puddly"
],
"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.36.8",
"pyserial==3.5",
"pyserial-asyncio==0.6",
"zha-quirks==0.0.106",
"zigpy-deconz==0.21.1",
"zigpy==0.59.0",
"zigpy-xbee==0.19.0",
"zigpy-zigate==0.11.0",
"zigpy-znp==0.11.6",
"universal-silabs-flasher==0.0.14",
"pyserial-asyncio-fast==0.11"
],
"usb": [
{
"vid": "10C4",
"pid": "EA60",
"description": "*2652*",
"known_devices": [
"slae.sh cc2652rb stick"
]
},
{
"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": "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": {
"config": {
"custom_quirks_path": "/config/custom_zha_quirks/",
"device_config": {},
"enable_quirks": true
},
"config_entry": {
"entry_id": "59ee77922cec06602094883ec9eba639",
"version": 3,
"domain": "zha",
"title": "SONOFF Zigbee 3.0 USB Dongle Plus V2, s/n: 20230221140743 - ITEAD",
"data": {
"device": {
"path": "/dev/serial/by-id/usb-ITEAD_SONOFF_Zigbee_3.0_USB_Dongle_Plus_V2_20230221140743-if00",
"baudrate": 115200,
"flow_control": "software"
},
"radio_type": "ezsp"
},
"options": {
"custom_configuration": {
"zha_options": {
"consider_unavailable_mains": 1740,
"default_light_transition": 0,
"enhanced_light_transition": false,
"light_transitioning_flag": true,
"always_prefer_xy_color_mode": true,
"group_members_assume_state": true,
"enable_identify_on_join": true,
"consider_unavailable_battery": 21600
}
}
},
"pref_disable_new_entities": false,
"pref_disable_polling": false,
"source": "user",
"unique_id": null,
"disabled_by": null
},
"application_state": {
"node_info": {
"nwk": 0,
"ieee": "**REDACTED**",
"logical_type": 0
},
"network_info": {
"extended_pan_id": "**REDACTED**",
"pan_id": 50423,
"nwk_update_id": 0,
"nwk_manager_id": 0,
"channel": 15,
"channel_mask": 134215680,
"security_level": 5,
"network_key": "**REDACTED**",
"tc_link_key": {
"key": [
90,
105,
103,
66,
101,
101,
65,
108,
108,
105,
97,
110,
99,
101,
48,
57
],
"tx_counter": 172032,
"rx_counter": 0,
"seq": 0,
"partner_ieee": "**REDACTED**"
},
"key_table": [],
"children": [],
"nwk_addresses": {},
"stack_specific": {
"ezsp": {
"hashed_tclk": "1f6b1cf67919462146c7475562bff867"
}
},
"metadata": {
"ezsp": {
"manufacturer": "",
"board": "",
"version": "6.10.3.0 build 297",
"stack_version": 8,
"can_burn_userdata_custom_eui64": false,
"can_rewrite_custom_eui64": false
}
},
"source": "bellows@0.36.8"
},
"counters": {
"controller_app_counters": {
"unicast_rx": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='unicast_rx', _raw_value=34, reset_count=0, _last_reset_value=0)"
},
"unicast_tx_success": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='unicast_tx_success', _raw_value=25, reset_count=0, _last_reset_value=0)"
},
"broadcast_tx_success_unexpected": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='broadcast_tx_success_unexpected', _raw_value=2, reset_count=0, _last_reset_value=0)"
},
"broadcast_rx": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='broadcast_rx', _raw_value=1, reset_count=0, _last_reset_value=0)"
},
"unicast_tx_failure": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='unicast_tx_failure', _raw_value=9, reset_count=0, _last_reset_value=0)"
}
},
"ezsp_counters": {
"MAC_RX_BROADCAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='MAC_RX_BROADCAST', _raw_value=87, reset_count=0, _last_reset_value=0)"
},
"MAC_TX_BROADCAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='MAC_TX_BROADCAST', _raw_value=36, reset_count=0, _last_reset_value=0)"
},
"MAC_RX_UNICAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='MAC_RX_UNICAST', _raw_value=72, reset_count=0, _last_reset_value=0)"
},
"MAC_TX_UNICAST_SUCCESS": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='MAC_TX_UNICAST_SUCCESS', _raw_value=54, reset_count=0, _last_reset_value=0)"
},
"MAC_TX_UNICAST_RETRY": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='MAC_TX_UNICAST_RETRY', _raw_value=19, reset_count=0, _last_reset_value=0)"
},
"MAC_TX_UNICAST_FAILED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='MAC_TX_UNICAST_FAILED', _raw_value=3, reset_count=0, _last_reset_value=0)"
},
"APS_DATA_RX_BROADCAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DATA_RX_BROADCAST', _raw_value=2, reset_count=0, _last_reset_value=0)"
},
"APS_DATA_TX_BROADCAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DATA_TX_BROADCAST', _raw_value=2, reset_count=0, _last_reset_value=0)"
},
"APS_DATA_RX_UNICAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DATA_RX_UNICAST', _raw_value=34, reset_count=0, _last_reset_value=0)"
},
"APS_DATA_TX_UNICAST_SUCCESS": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DATA_TX_UNICAST_SUCCESS', _raw_value=25, reset_count=0, _last_reset_value=0)"
},
"APS_DATA_TX_UNICAST_RETRY": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DATA_TX_UNICAST_RETRY', _raw_value=12, reset_count=0, _last_reset_value=0)"
},
"APS_DATA_TX_UNICAST_FAILED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DATA_TX_UNICAST_FAILED', _raw_value=6, reset_count=0, _last_reset_value=0)"
},
"ROUTE_DISCOVERY_INITIATED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='ROUTE_DISCOVERY_INITIATED', _raw_value=8, reset_count=0, _last_reset_value=0)"
},
"NEIGHBOR_ADDED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='NEIGHBOR_ADDED', _raw_value=5, reset_count=0, _last_reset_value=0)"
},
"NEIGHBOR_REMOVED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='NEIGHBOR_REMOVED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"NEIGHBOR_STALE": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='NEIGHBOR_STALE', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"JOIN_INDICATION": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='JOIN_INDICATION', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"CHILD_REMOVED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='CHILD_REMOVED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"ASH_OVERFLOW_ERROR": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='ASH_OVERFLOW_ERROR', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"ASH_FRAMING_ERROR": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='ASH_FRAMING_ERROR', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"ASH_OVERRUN_ERROR": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='ASH_OVERRUN_ERROR', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"NWK_FRAME_COUNTER_FAILURE": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='NWK_FRAME_COUNTER_FAILURE', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"APS_FRAME_COUNTER_FAILURE": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_FRAME_COUNTER_FAILURE', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"UTILITY": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='UTILITY', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"APS_LINK_KEY_NOT_AUTHORIZED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_LINK_KEY_NOT_AUTHORIZED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"NWK_DECRYPTION_FAILURE": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='NWK_DECRYPTION_FAILURE', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"APS_DECRYPTION_FAILURE": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='APS_DECRYPTION_FAILURE', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"ALLOCATE_PACKET_BUFFER_FAILURE": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='ALLOCATE_PACKET_BUFFER_FAILURE', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"RELAYED_UNICAST": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='RELAYED_UNICAST', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PHY_TO_MAC_QUEUE_LIMIT_REACHED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PHY_TO_MAC_QUEUE_LIMIT_REACHED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PACKET_VALIDATE_LIBRARY_DROPPED_COUNT": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PACKET_VALIDATE_LIBRARY_DROPPED_COUNT', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"TYPE_NWK_RETRY_OVERFLOW": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='TYPE_NWK_RETRY_OVERFLOW', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PHY_CCA_FAIL_COUNT": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PHY_CCA_FAIL_COUNT', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"BROADCAST_TABLE_FULL": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='BROADCAST_TABLE_FULL', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PTA_LO_PRI_REQUESTED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PTA_LO_PRI_REQUESTED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PTA_HI_PRI_REQUESTED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PTA_HI_PRI_REQUESTED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PTA_LO_PRI_DENIED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PTA_LO_PRI_DENIED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PTA_HI_PRI_DENIED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PTA_HI_PRI_DENIED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PTA_LO_PRI_TX_ABORTED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PTA_LO_PRI_TX_ABORTED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"PTA_HI_PRI_TX_ABORTED": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='PTA_HI_PRI_TX_ABORTED', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"ADDRESS_CONFLICT_SENT": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='ADDRESS_CONFLICT_SENT', _raw_value=0, reset_count=0, _last_reset_value=0)"
},
"EZSP_FREE_BUFFERS": {
"__type": "<class 'zigpy.state.Counter'>",
"repr": "Counter(name='EZSP_FREE_BUFFERS', _raw_value=240, reset_count=0, _last_reset_value=0)"
}
}
},
"broadcast_counters": {},
"device_counters": {},
"group_counters": {}
},
"energy_scan": {
"11": 31.01324838787301,
"12": 33.860880820104335,
"13": 55.9836862725909,
"14": 33.860880820104335,
"15": 28.30261646762903,
"16": 28.30261646762903,
"17": 31.01324838787301,
"18": 39.90320178295578,
"19": 92.95959997754716,
"20": 92.95959997754716,
"21": 96.19660508390695,
"22": 88.70042934643088,
"23": 82.35373987514762,
"24": 87.33047519856483,
"25": 31.01324838787301,
"26": 73.50699819621309
},
"versions": {
"bellows": "0.36.8",
"zigpy": "0.59.0",
"zigpy_deconz": "0.21.1",
"zigpy_xbee": "0.19.0",
"zigpy_znp": "0.11.6",
"zigpy_zigate": "0.11.0",
"zhaquirks": "0.0.106"
}
}
}
"""ME167 TRV devices support."""
import logging
from typing import Optional, Union
import zigpy.types as t
from zhaquirks import Bus, LocalDataCluster
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
TuyaManufCluster,
TuyaManufClusterAttributes,
TuyaPowerConfigurationCluster,
TuyaThermostat,
TuyaThermostatCluster,
TuyaTimePayload,
TuyaUserInterfaceCluster,
)
from zigpy.profiles import zha
from zigpy.zcl import foundation
from zigpy.zcl.clusters.general import (
AnalogOutput,
Basic,
BinaryInput,
Groups,
Identify,
OnOff,
Ota,
Scenes,
Time,
)
from zigpy.zcl.clusters.hvac import Thermostat
_LOGGER = logging.getLogger(__name__)
ME167_TEMPERATURE_ATTR = 0x0205 # [0, 0, 0, 210] current room temp (decidegree)
ME167_TARGET_TEMP_ATTR = 0x0204 # [0, 0, 0, 190] target room temp (decidegree)
ME167_TEMP_CALIBRATION_ATTR = 0x022F # (decidegree)
ME167_CHILD_LOCK_ATTR = 0x0107 # [0] unlocked [1] child-locked
ME167_BATTERY_STATE_ATTR = 0x0523 # [0] OK [1] Empty
ME167_MODE_ATTR = 0x0402 # [0] auto [1] heat [2] off
ME167_STATE_ATTR = 0x0403 # [1] idle [0] heating /!\ inverted
# minimum limit of temperature setting
ME167_MIN_TEMPERATURE_VAL = 5 # degrees
# maximum limit of temperature setting
ME167_MAX_TEMPERATURE_VAL = 35 # degrees
ME167ManufClusterSelf = {}
class CustomTuyaOnOff(LocalDataCluster, OnOff):
"""Custom Tuya OnOff cluster."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self.endpoint.device.thermostat_onoff_bus.add_listener(self)
# pylint: disable=R0201
def map_attribute(self, attribute, value):
"""Map standardized attribute value to dict of manufacturer values."""
return {}
async def write_attributes(self, attributes, manufacturer=None):
"""Implement writeable attributes."""
records = self._write_attr_records(attributes)
if not records:
return [[foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)]]
manufacturer_attrs = {}
for record in records:
attr_name = self.attributes[record.attrid][0]
new_attrs = self.map_attribute(attr_name, record.value.value)
_LOGGER.debug(
"[0x%04x:%s:0x%04x] Mapping standard %s (0x%04x) "
"with value %s to custom %s",
self.endpoint.device.nwk,
self.endpoint.endpoint_id,
self.cluster_id,
attr_name,
record.attrid,
repr(record.value.value),
repr(new_attrs),
)
manufacturer_attrs.update(new_attrs)
if not manufacturer_attrs:
return [
[
foundation.WriteAttributesStatusRecord(
foundation.Status.FAILURE, r.attrid
)
for r in records
]
]
await ME167ManufClusterSelf[
self.endpoint.device.ieee
].endpoint.tuya_manufacturer.write_attributes(
manufacturer_attrs, manufacturer=manufacturer
)
return [[foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)]]
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."""
if command_id in (0x0000, 0x0001, 0x0002):
if command_id == 0x0000:
value = False
elif command_id == 0x0001:
value = True
else:
attrid = self.attributes_by_name["on_off"].id
success, _ = await self.read_attributes(
(attrid,), manufacturer=manufacturer
)
try:
value = success[attrid]
except KeyError:
return foundation.Status.FAILURE
value = not value
(res,) = await self.write_attributes(
{"on_off": value},
manufacturer=manufacturer,
)
return [command_id, res[0].status]
return [command_id, foundation.Status.UNSUP_CLUSTER_COMMAND]
class ME167ManufCluster(TuyaManufClusterAttributes):
"""Manufacturer Specific Cluster of some thermostatic valves."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self.endpoint.device.ME167ManufCluster_bus.add_listener(self)
global ME167ManufClusterSelf
ME167ManufClusterSelf[self.endpoint.device.ieee] = self
server_commands = {
0x0000: foundation.ZCLCommandDef(
"set_data",
{"param": TuyaManufCluster.Command},
False,
is_manufacturer_specific=False,
),
0x0010: foundation.ZCLCommandDef(
"mcu_version_req",
{"param": t.uint16_t},
False,
is_manufacturer_specific=True,
),
0x0024: foundation.ZCLCommandDef(
"set_time",
{"param": TuyaTimePayload},
False,
is_manufacturer_specific=True,
),
}
attributes = TuyaManufClusterAttributes.attributes.copy()
attributes.update(
{
ME167_TEMPERATURE_ATTR: ("temperature", t.uint32_t, True),
ME167_TARGET_TEMP_ATTR: ("target_temperature", t.uint32_t, True),
ME167_TEMP_CALIBRATION_ATTR: ("target_temperature", t.int32s, True),
ME167_CHILD_LOCK_ATTR: ("child_lock", t.uint8_t, True),
ME167_MODE_ATTR: ("mode", t.uint8_t, True),
ME167_STATE_ATTR: ("state", t.uint8_t, True),
ME167_BATTERY_STATE_ATTR: ("battery_state", t.uint8_t, True),
}
)
TEMPERATURE_ATTRS = {
ME167_TEMPERATURE_ATTR: ("local_temperature", lambda value: value * 10),
ME167_TARGET_TEMP_ATTR: (
"occupied_heating_setpoint",
lambda value: value * 10,
),
ME167_TEMP_CALIBRATION_ATTR: (
"local_temperature_calibration",
None,
),
}
def _update_attribute(self, attrid, value):
super()._update_attribute(attrid, value)
if attrid in self.TEMPERATURE_ATTRS:
self.endpoint.device.thermostat_bus.listener_event(
"temperature_change",
self.TEMPERATURE_ATTRS[attrid][0],
value
if self.TEMPERATURE_ATTRS[attrid][1] is None
else self.TEMPERATURE_ATTRS[attrid][1](value),
)
elif attrid == ME167_MODE_ATTR:
self.endpoint.device.thermostat_bus.listener_event("mode_change", value)
elif attrid == ME167_CHILD_LOCK_ATTR:
self.endpoint.device.ui_bus.listener_event("child_lock_change", value)
self.endpoint.device.thermostat_onoff_bus.listener_event(
"child_lock_change", value
)
elif attrid == ME167_STATE_ATTR:
self.endpoint.device.thermostat_bus.listener_event(
"hass_climate_state_change", value
)
elif attrid == ME167_BATTERY_STATE_ATTR:
self.endpoint.device.battery_bus.listener_event(
"battery_change", 0 if value == 1 else 100
)
elif attrid == ME167_TEMP_CALIBRATION_ATTR:
self.endpoint.device.ME167TempCalibration_bus.listener_event(
"set_value", value
)
class ME167Thermostat(TuyaThermostatCluster):
"""Thermostat cluster for some thermostatic valves."""
class Preset(t.enum8):
"""Working modes of the thermostat."""
Away = 0x00
Schedule = 0x01
Manual = 0x02
Comfort = 0x03
Eco = 0x04
Boost = 0x05
Complex = 0x06
_CONSTANT_ATTRIBUTES = {
0x001B: Thermostat.ControlSequenceOfOperation.Heating_Only,
}
attributes = TuyaThermostatCluster.attributes.copy()
attributes.update(
{
0x4000: ("operation_preset", Preset, True),
}
)
DIRECT_MAPPING_ATTRS = {
"occupied_heating_setpoint": (
ME167_TARGET_TEMP_ATTR,
lambda value: round(value / 10),
),
"operation_preset": (ME167_MODE_ATTR, None),
"local_temperature_calibration": (
ME167_TEMP_CALIBRATION_ATTR,
None,
),
}
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self.endpoint.device.thermostat_bus.add_listener(self)
self.endpoint.device.thermostat_bus.listener_event(
"temperature_change",
"min_heat_setpoint_limit",
ME167_MIN_TEMPERATURE_VAL * 100,
)
self.endpoint.device.thermostat_bus.listener_event(
"temperature_change",
"max_heat_setpoint_limit",
ME167_MAX_TEMPERATURE_VAL * 100,
)
def map_attribute(self, attribute, value):
"""Map standardized attribute value to dict of manufacturer values."""
if attribute in self.DIRECT_MAPPING_ATTRS:
return {
self.DIRECT_MAPPING_ATTRS[attribute][0]: value
if self.DIRECT_MAPPING_ATTRS[attribute][1] is None
else self.DIRECT_MAPPING_ATTRS[attribute][1](value)
}
if attribute in ("system_mode", "programing_oper_mode"):
if attribute == "system_mode":
system_mode = value
oper_mode = self._attr_cache.get(
self.attributes_by_name["programing_oper_mode"].id,
self.ProgrammingOperationMode.Simple,
)
else:
system_mode = self._attr_cache.get(
self.attributes_by_name["system_mode"].id, self.SystemMode.Heat
)
oper_mode = value
if system_mode == self.SystemMode.Off:
return {ME167_MODE_ATTR: 2}
if system_mode == self.SystemMode.Heat:
if oper_mode == self.ProgrammingOperationMode.Schedule_programming_mode:
return {ME167_MODE_ATTR: 0}
if oper_mode == self.ProgrammingOperationMode.Simple:
return {ME167_MODE_ATTR: 1}
self.error("Unsupported value for ProgrammingOperationMode")
else:
self.error("Unsupported value for SystemMode")
def hass_climate_state_change(self, value):
"""Update of the HASS Climate gui state."""
self.endpoint.device.thermostat_bus.listener_event("state_change", not value)
def mode_change(self, value):
"""System Mode change."""
if value == 0:
operation_preset = self.Preset.Schedule
prog_mode = self.ProgrammingOperationMode.Schedule_programming_mode
occupancy = self.Occupancy.Occupied
system_mode = self.SystemMode.Heat
elif value == 1:
operation_preset = self.Preset.Manual
prog_mode = self.ProgrammingOperationMode.Simple
occupancy = self.Occupancy.Occupied
system_mode = self.SystemMode.Heat
elif value == 2:
operation_preset = self.Preset.Manual
prog_mode = self.ProgrammingOperationMode.Simple
occupancy = self.Occupancy.Occupied
system_mode = self.SystemMode.Off
self._update_attribute(self.attributes_by_name["system_mode"].id, system_mode)
self._update_attribute(
self.attributes_by_name["programing_oper_mode"].id, prog_mode
)
self._update_attribute(self.attributes_by_name["occupancy"].id, occupancy)
self._update_attribute(
self.attributes_by_name["operation_preset"].id, operation_preset
)
class ME167UserInterface(TuyaUserInterfaceCluster):
"""HVAC User interface cluster for tuya electric heating thermostats."""
_CHILD_LOCK_ATTR = ME167_CHILD_LOCK_ATTR
class ME167ChildLock(CustomTuyaOnOff):
"""On/Off cluster for the child lock function."""
def child_lock_change(self, value):
"""Child lock change."""
self._update_attribute(self.attributes_by_name["on_off"].id, value)
def map_attribute(self, attribute, value):
"""Map standardized attribute value to dict of manufacturer values."""
if attribute == "on_off":
return {ME167_CHILD_LOCK_ATTR: value}
class ME167TempCalibration(LocalDataCluster, AnalogOutput):
"""Analog output for Temp Calibration."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self.endpoint.device.ME167TempCalibration_bus.add_listener(self)
self._update_attribute(
self.attributes_by_name["description"].id, "Temperature Calibration"
)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 10)
self._update_attribute(self.attributes_by_name["min_present_value"].id, -10)
self._update_attribute(self.attributes_by_name["resolution"].id, 1)
self._update_attribute(self.attributes_by_name["application_type"].id, 13 << 16)
self._update_attribute(self.attributes_by_name["engineering_units"].id, 62)
def set_value(self, value):
"""Set value."""
self._update_attribute(self.attributes_by_name["present_value"].id, value)
def get_value(self):
"""Get value."""
return self._attr_cache.get(self.attributes_by_name["present_value"].id)
async def write_attributes(self, attributes, manufacturer=None):
"""Override the default Cluster write_attributes."""
for attrid, value in attributes.items():
if isinstance(attrid, str):
attrid = self.attributes_by_name[attrid].id
if attrid not in self.attributes:
self.error("%d is not a valid attribute id", attrid)
continue
self._update_attribute(attrid, value)
await ME167ManufClusterSelf[
self.endpoint.device.ieee
].endpoint.tuya_manufacturer.write_attributes(
{ME167_TEMP_CALIBRATION_ATTR: value},
manufacturer=None,
)
return ([foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)],)
class ME167(TuyaThermostat):
"""ME167 Thermostatic radiator valve and clones."""
def __init__(self, *args, **kwargs):
"""Init device."""
self.thermostat_onoff_bus = Bus()
self.ME167ManufCluster_bus = Bus()
self.ME167TempCalibration_bus = Bus()
super().__init__(*args, **kwargs)
signature = {
# "endpoints": {
# "1": {
# "profile_id": 260,
# "device_type": "0x0051",
# "in_clusters": [
# "0x0000",
# "0x0004",
# "0x0005",
# "0xef00"
# ],
# "out_clusters": [
# "0x000a",
# "0x0019"
# ]
# }
# },
MODELS_INFO: [
("_TZE200_bvu2wnxz", "TS0601"),
("_TZE200_6rdj8dzm", "TS0601"),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaManufClusterAttributes.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
}
},
}
replacement = {
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.THERMOSTAT,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
ME167ManufCluster,
ME167Thermostat,
ME167UserInterface,
TuyaPowerConfigurationCluster,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
INPUT_CLUSTERS: [
ME167ChildLock,
],
OUTPUT_CLUSTERS: [],
},
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.CONSUMPTION_AWARENESS_DEVICE,
INPUT_CLUSTERS: [ME167TempCalibration],
OUTPUT_CLUSTERS: [],
},
}
}
{
"backup_time": "2023-11-07T13:12:01.332666+00:00",
"network_info": {
"extended_pan_id": "c2:a5:85:19:47:5c:f8:28",
"pan_id": "C4F7",
"nwk_update_id": 0,
"nwk_manager_id": "0000",
"channel": 15,
"channel_mask": [
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26
],
"security_level": 5,
"network_key": {
"key": "e2:b2:87:58:77:4a:31:56:dc:04:eb:62:fa:44:2d:b2",
"tx_counter": 12660830,
"rx_counter": 0,
"seq": 0,
"partner_ieee": "ff:ff:ff:ff:ff:ff:ff:ff"
},
"tc_link_key": {
"key": "5a:69:67:42:65:65:41:6c:6c:69:61:6e:63:65:30:39",
"tx_counter": 172032,
"rx_counter": 0,
"seq": 0,
"partner_ieee": "00:12:4b:00:1c:dd:14:58"
},
"key_table": [],
"children": [],
"nwk_addresses": {
"84:ba:20:ff:fe:cd:a1:21": "565C",
"60:b6:47:ff:fe:54:11:79": "8F6E"
},
"stack_specific": {
"ezsp": {
"hashed_tclk": "1f6b1cf67919462146c7475562bff867"
}
},
"metadata": {
"ezsp": {
"manufacturer": "",
"board": "",
"version": "6.10.3.0 build 297",
"stack_version": 8,
"can_burn_userdata_custom_eui64": false,
"can_rewrite_custom_eui64": false
}
},
"source": "bellows@0.36.8"
},
"node_info": {
"nwk": "0000",
"ieee": "00:12:4b:00:1c:dd:14:58",
"logical_type": "coordinator"
}
}
{
"home_assistant": {
"installation_type": "Home Assistant OS",
"version": "2023.11.1",
"dev": false,
"hassio": true,
"virtualenv": false,
"python_version": "3.11.6",
"docker": true,
"arch": "aarch64",
"timezone": "Europe/Berlin",
"os_name": "Linux",
"os_version": "6.1.21-v8",
"supervisor": "2023.10.1",
"host_os": "Home Assistant OS 11.1",
"docker_version": "24.0.6",
"chassis": "embedded",
"run_as_root": true
},
"custom_components": {
"deutschebahn": {
"version": "2.0.4",
"requirements": [
"schiene==0.26"
]
},
"dht": {
"version": "2022.4.0",
"requirements": [
"adafruit-circuitpython-dht==3.7.0",
"RPi.GPIO==0.7.1",
"tenacity==8.0.1"
]
},
"smartir": {
"version": "1.17.8",
"requirements": [
"aiofiles>=0.6.0"
]
},
"rpi_gpio": {
"version": "2022.7.0",
"requirements": [
"RPi.GPIO==0.7.1"
]
},
"openrgb": {
"version": "2.3",
"requirements": [
"openrgb-python==0.2.10"
]
},
"openai_response": {
"version": "0.1.0",
"requirements": [
"openai==0.27.0"
]
},
"garbage_collection": {
"version": "4.10.2",
"requirements": [
"python-dateutil>=2.8.2"
]
},
"chatgpt": {
"version": "1.0.0",
"requirements": [
"requests>=2.28.2"
]
},
"overwolfstatus": {
"version": "1.0.1",
"requirements": []
},
"hass_agent": {
"version": "2022.11.9",
"requirements": []
},
"ytube_music_player": {
"version": "20230321.01",
"requirements": [
"ytmusicapi==0.25.0",
"pytube==10.5.1",
"integrationhelper==0.2.2"
]
},
"localtuya": {
"version": "5.2.1",
"requirements": []
},
"climate_template": {
"version": "0.6.1",
"requirements": []
},
"watchman": {
"version": "0.5.1",
"requirements": [
"prettytable==3.0.0"
]
},
"hacs": {
"version": "1.33.0",
"requirements": [
"aiogithubapi>=22.10.1"
]
}
},
"integration_manifest": {
"domain": "zha",
"name": "Zigbee Home Automation",
"after_dependencies": [
"onboarding",
"usb"
],
"codeowners": [
"@dmulcahey",
"@adminiuga",
"@puddly"
],
"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.36.8",
"pyserial==3.5",
"pyserial-asyncio==0.6",
"zha-quirks==0.0.106",
"zigpy-deconz==0.21.1",
"zigpy==0.59.0",
"zigpy-xbee==0.19.0",
"zigpy-zigate==0.11.0",
"zigpy-znp==0.11.6",
"universal-silabs-flasher==0.0.14",
"pyserial-asyncio-fast==0.11"
],
"usb": [
{
"vid": "10C4",
"pid": "EA60",
"description": "*2652*",
"known_devices": [
"slae.sh cc2652rb stick"
]
},
{
"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": "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": 6538,
"manufacturer": "_TZE200_bvu2wnxz",
"model": "TS0601",
"name": "_TZE200_bvu2wnxz TS0601",
"quirk_applied": false,
"quirk_class": "zigpy.device.Device",
"quirk_id": null,
"manufacturer_code": 4098,
"power_source": "Battery or Unknown",
"lqi": 168,
"rssi": -58,
"last_seen": "2023-11-07T14:10:44",
"available": true,
"device_type": "EndDevice",
"signature": {
"node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4098, 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=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)",
"endpoints": {
"1": {
"profile_id": "0x0104",
"device_type": "0x0051",
"input_clusters": [
"0x0000",
"0x0004",
"0x0005",
"0xef00"
],
"output_clusters": [
"0x000a",
"0x0019"
]
}
},
"manufacturer": "_TZE200_bvu2wnxz",
"model": "TS0601"
},
"active_coordinator": false,
"entities": [],
"neighbors": [],
"routes": [],
"endpoint_names": [
{
"name": "SMART_PLUG"
}
],
"user_given_name": "Timos Heizung",
"device_reg_id": "a7dcec19a615085d7a0e0569f51471c8",
"area_id": "timos_zimmer",
"cluster_details": {
"1": {
"device_type": {
"name": "SMART_PLUG",
"id": 81
},
"profile_id": 260,
"in_clusters": {
"0x0000": {
"endpoint_attribute": "basic",
"attributes": {
"0x0001": {
"attribute_name": "app_version",
"value": 65
},
"0x0004": {
"attribute_name": "manufacturer",
"value": "_TZE200_bvu2wnxz"
},
"0x0005": {
"attribute_name": "model",
"value": "TS0601"
}
},
"unsupported_attributes": {}
},
"0x0004": {
"endpoint_attribute": "groups",
"attributes": {},
"unsupported_attributes": {}
},
"0x0005": {
"endpoint_attribute": "scenes",
"attributes": {},
"unsupported_attributes": {}
},
"0xef00": {
"endpoint_attribute": null,
"attributes": {},
"unsupported_attributes": {}
}
},
"out_clusters": {
"0x0019": {
"endpoint_attribute": "ota",
"attributes": {},
"unsupported_attributes": {}
},
"0x000a": {
"endpoint_attribute": "time",
"attributes": {},
"unsupported_attributes": {}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment