11import logging
22
33from homeassistant .components .switch import SwitchEntity
4- from homeassistant .const import Platform
54from homeassistant .core import HomeAssistant
65from homeassistant .helpers .update_coordinator import CoordinatorEntity
76
87from .const import DOMAIN
98from .coordinator import CompitDataUpdateCoordinator
10- from .sensor_matcher import SensorMatcher
119from .types .DeviceDefinitions import Parameter
1210from .types .SystemInfo import Device
1311
14- _LOGGER : logging .Logger = logging .getLogger (__package__ )
15-
16-
17- async def async_setup_entry (hass : HomeAssistant , entry , async_add_devices ):
18- """
19- Sets up the switch platform for a specific entry in Home Assistant.
20-
21- This function initializes and adds switch devices dynamically based on the
22- provided entry, using the data from the specified coordinator object. The
23- devices are filtered according to their type, platform compatibility, and available
24- parameters.
25-
26- Args:
27- hass (HomeAssistant): The Home Assistant core object.
28- entry: The configuration entry for the integration.
29- async_add_devices: Callback function to add devices to Home Assistant.
30-
31- """
32- coordinator : CompitDataUpdateCoordinator = hass .data [DOMAIN ][entry .entry_id ]
33- async_add_devices (
34- [
35- CompitSwitch (coordinator , device , parameter , device_definition .name )
36- for gate in coordinator .gates
37- for device in gate .devices
38- if (
39- device_definition := next (
40- (
41- definition
42- for definition in coordinator .device_definitions .devices
43- if definition .code == device .type
44- ),
45- None ,
46- )
47- )
48- is not None
49- for parameter in device_definition .parameters
50- if SensorMatcher .get_platform (
51- parameter ,
52- coordinator .data [device .id ].state .get_parameter_value (parameter ),
53- )
54- == Platform .SWITCH
55- ]
56- )
12+ _LOGGER = logging .getLogger (__name__ )
5713
5814
5915class CompitSwitch (CoordinatorEntity , SwitchEntity ):
@@ -66,43 +22,34 @@ def __init__(
6622 ):
6723 super ().__init__ (coordinator )
6824 self .coordinator = coordinator
69- # Use a "switch_" prefix for clarity
7025 self .unique_id = f"switch_{ device .label } { parameter .parameter_code } "
7126 self .label = f"{ device .label } { parameter .label } "
7227 self .parameter = parameter
7328 self .device = device
7429 self .device_name = device_name
75-
76- # Initialize boolean state
7730 self ._is_on : bool = False
7831
79- # Safely read current value from coordinator
80- data_entry = (
81- self .coordinator .data .get (self .device .id )
82- if hasattr (self .coordinator , "data" )
83- else None
84- )
32+ # Initialize from coordinator data safely (state may be bool or DeviceState)
33+ data_entry = getattr (self .coordinator , "data" , {}).get (self .device .id )
8534 state_obj = (
8635 getattr (data_entry , "state" , None ) if data_entry is not None else None
8736 )
8837
89- # If a state is already a boolean, use it directly
9038 if isinstance (state_obj , bool ):
9139 self ._is_on = state_obj
92- # If a state has get_parameter_value, resolve the parameter
9340 elif hasattr (state_obj , "get_parameter_value" ):
94- value = state_obj .get_parameter_value (self .parameter )
41+ try :
42+ value = state_obj .get_parameter_value (self .parameter )
43+ except Exception : # defensive: unexpected state shape
44+ value = None
9545 if value is not None :
96- # Prefer numeric/boolean value when present
9746 raw_val = getattr (value , "value" , None )
9847 if raw_val is not None :
99- # Coerce to boolean
10048 try :
101- self ._is_on = bool (int (raw_val )) # handles "0"/"1"/0/1
49+ self ._is_on = bool (int (raw_val ))
10250 except Exception :
10351 self ._is_on = bool (raw_val )
10452 else :
105- # Fall back to matching by value_code against parameter details
10653 vcode = getattr (value , "value_code" , None )
10754 details = self .parameter .details or []
10855 matched = next (
@@ -127,21 +74,39 @@ def name(self):
12774
12875 @property
12976 def is_on (self ):
77+ # Try to reflect latest coordinator value if available
78+ try :
79+ data_entry = getattr (self .coordinator , "data" , {}).get (self .device .id )
80+ state_obj = (
81+ getattr (data_entry , "state" , None ) if data_entry is not None else None
82+ )
83+ if isinstance (state_obj , bool ):
84+ return state_obj
85+ if hasattr (state_obj , "get_parameter_value" ):
86+ value = state_obj .get_parameter_value (self .parameter )
87+ if value is not None :
88+ raw_val = getattr (value , "value" , None )
89+ if raw_val is not None :
90+ try :
91+ return bool (int (raw_val ))
92+ except Exception :
93+ return bool (raw_val )
94+ except Exception :
95+ # fall back to cached flag
96+ pass
13097 return self ._is_on
13198
13299 @property
133100 def extra_state_attributes (self ):
134- items = [
135- {
136- "device" : self .device .label ,
137- "device_id" : self .device .id ,
138- "device_class" : self .device .class_ ,
139- "device_type" : self .device .type ,
140- }
141- ]
142-
143101 return {
144- "details" : items ,
102+ "details" : [
103+ {
104+ "device" : self .device .label ,
105+ "device_id" : self .device .id ,
106+ "device_class" : self .device .class_ ,
107+ "device_type" : self .device .type ,
108+ }
109+ ],
145110 }
146111
147112 async def async_turn_on (self , ** kwargs ):
@@ -173,3 +138,61 @@ async def async_toggle(self, **kwargs):
173138 await self .async_turn_off ()
174139 else :
175140 await self .async_turn_on ()
141+
142+
143+ # ... existing code ...
144+
145+
146+ async def async_setup_entry (hass : HomeAssistant , entry , async_add_entities ):
147+ coordinator : CompitDataUpdateCoordinator = hass .data [DOMAIN ][entry .entry_id ]
148+ entities = []
149+ for gate in coordinator .gates :
150+ for device in gate .devices :
151+ device_definition = next (
152+ (
153+ d
154+ for d in coordinator .device_definitions .devices
155+ if d .code == device .type
156+ ),
157+ None ,
158+ )
159+ if device_definition is None :
160+ continue
161+
162+ # Safely inspect current state
163+ data_entry = getattr (coordinator , "data" , {}).get (device .id )
164+ state_obj = (
165+ getattr (data_entry , "state" , None ) if data_entry is not None else None
166+ )
167+
168+ for parameter in device_definition .parameters :
169+ # Only writable, non-number, non-select -> treat as switch
170+ is_writable = getattr (parameter , "readWrite" , "R" ) != "R"
171+ is_number_like = (
172+ parameter .min_value is not None and parameter .max_value is not None
173+ )
174+ is_select_like = parameter .details is not None
175+ if not is_writable or is_number_like or is_select_like :
176+ continue
177+
178+ # If state is a DeviceState, check visibility; if bool or None, skip the check
179+ visible = True
180+ if hasattr (state_obj , "get_parameter_value" ):
181+ try :
182+ v = state_obj .get_parameter_value (parameter )
183+ visible = v is not None and not getattr (v , "hidden" , False )
184+ except Exception :
185+ visible = False
186+
187+ if visible :
188+ entities .append (
189+ CompitSwitch (
190+ coordinator = coordinator ,
191+ device = device ,
192+ parameter = parameter ,
193+ device_name = device_definition .name ,
194+ )
195+ )
196+
197+ if entities :
198+ async_add_entities (entities )
0 commit comments