diff --git a/custom_components/ds_air/__init__.py b/custom_components/ds_air/__init__.py index 34ce21c..dc4e0b0 100644 --- a/custom_components/ds_air/__init__.py +++ b/custom_components/ds_air/__init__.py @@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant from .hass_inst import GetHass -from .const import CONF_GW, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_GW, DOMAIN +from .const import CONF_GW, CONF_ID, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_GW, DOMAIN from .ds_air_service.config import Config _LOGGER = logging.getLogger(__name__) @@ -46,7 +46,8 @@ async def async_setup_entry( Config.is_c611 = gw == DEFAULT_GW from .ds_air_service.service import Service - await hass.async_add_executor_job(Service.init, host, port, scan_interval) + instance_id = entry.data[CONF_ID] # Use CONF_ID as the instance ID + await hass.async_add_executor_job(Service.init, instance_id, host, port, scan_interval) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(update_listener)) @@ -59,7 +60,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN].get("listener")() unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) from .ds_air_service.service import Service - Service.destroy() + instance_id = entry.data[CONF_ID] # Use CONF_ID as the instance ID + Service.destroy(instance_id) return unload_ok diff --git a/custom_components/ds_air/climate.py b/custom_components/ds_air/climate.py index 2c5e9fb..dd99eeb 100644 --- a/custom_components/ds_air/climate.py +++ b/custom_components/ds_air/climate.py @@ -25,7 +25,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_state_change_event -from .const import DOMAIN +from .const import DOMAIN, CONF_ID from .ds_air_service.config import Config from .ds_air_service.ctrl_enum import EnumControl from .ds_air_service.dao import AirCon, AirConStatus @@ -56,10 +56,11 @@ async def async_setup_entry( ) -> None: """Set up the climate devices.""" + instance_id = entry.data[CONF_ID] # Use CONF_ID as the instance ID from .ds_air_service.service import Service climates = [] - for aircon in Service.get_aircons(): - climates.append(DsAir(aircon)) + for aircon in Service.get_aircons(instance_id): + climates.append(DsAir(aircon, instance_id)) async_add_entities(climates) link = entry.options.get("link") sensor_temp_map: dict[str, list[DsAir]] = {} @@ -93,7 +94,7 @@ class DsAir(ClimateEntity): _enable_turn_on_off_backwards_compatibility = False # used in 2024.2~2024.12 - def __init__(self, aircon: AirCon): + def __init__(self, aircon: AirCon, instance_id: str): _log('create aircon:') _log(str(aircon.__dict__)) _log(str(aircon.status.__dict__)) @@ -107,6 +108,7 @@ def __init__(self, aircon: AirCon): self._link_cur_humi = False self._cur_temp = None self._cur_humi = None + self._instance_id = instance_id from .ds_air_service.service import Service Service.register_status_hook(aircon, self._status_change_hook) @@ -331,7 +333,7 @@ def set_temperature(self, **kwargs): status.setted_temp = round(kwargs.get(ATTR_TEMPERATURE) * 10.0) new_status.setted_temp = round(kwargs.get(ATTR_TEMPERATURE) * 10.0) from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) self.schedule_update_ha_state() def set_humidity(self, humidity): @@ -343,7 +345,7 @@ def set_humidity(self, humidity): status.humidity = EnumControl.Humidity(humidity) new_status.humidity = EnumControl.Humidity(humidity) from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) self.schedule_update_ha_state() def set_fan_mode(self, fan_mode): @@ -355,7 +357,7 @@ def set_fan_mode(self, fan_mode): status.air_flow = EnumControl.get_air_flow_enum(fan_mode) new_status.air_flow = EnumControl.get_air_flow_enum(fan_mode) from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) self.schedule_update_ha_state() def set_hvac_mode(self, hvac_mode: str) -> None: @@ -367,7 +369,7 @@ def set_hvac_mode(self, hvac_mode: str) -> None: status.switch = EnumControl.Switch.OFF new_status.switch = EnumControl.Switch.OFF from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) else: status.switch = EnumControl.Switch.ON new_status.switch = EnumControl.Switch.ON @@ -399,7 +401,7 @@ def set_hvac_mode(self, hvac_mode: str) -> None: status.mode = mode new_status.mode = mode from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) self.schedule_update_ha_state() def set_swing_mode(self, swing_mode): @@ -412,7 +414,7 @@ def set_swing_mode(self, swing_mode): status.fan_direction2 = EnumControl.get_fan_direction_enum(swing_mode) new_status.fan_direction2 = EnumControl.get_fan_direction_enum(swing_mode) from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) self.schedule_update_ha_state() def set_preset_mode(self, preset_mode: str) -> None: @@ -436,7 +438,7 @@ def set_preset_mode(self, preset_mode: str) -> None: status.mode = mode new_status.mode = mode from .ds_air_service.service import Service - Service.control(self._device_info, new_status) + Service.control(self._instance_id, self._device_info, new_status) self.schedule_update_ha_state() def turn_aux_heat_on(self) -> None: diff --git a/custom_components/ds_air/config_flow.py b/custom_components/ds_air/config_flow.py index d70f9d9..87f6bef 100644 --- a/custom_components/ds_air/config_flow.py +++ b/custom_components/ds_air/config_flow.py @@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult -from .const import CONF_GW, DEFAULT_GW, DEFAULT_HOST, DEFAULT_PORT, DOMAIN, GW_LIST +from .const import CONF_GW, CONF_ID, DEFAULT_ID, DEFAULT_GW, DEFAULT_HOST, DEFAULT_PORT, DOMAIN, GW_LIST from .ds_air_service.service import Service from .hass_inst import GetHass @@ -42,14 +42,12 @@ def __init__(self): self.user_input = {} async def async_step_user(self, user_input: dict[str, Any] | None = None) -> FlowResult: - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - - errors = {} if user_input is not None: + instance_id = self.context.get("instance_id", self.flow_id) + self.context["instance_id"] = instance_id self.user_input.update(user_input) if user_input.get(CONF_SENSORS) == False or user_input.get("temp") is not None: - return self.async_create_entry(title="金制空气", data=self.user_input) + return self.async_create_entry(title="金制空气@"+str(self.user_input[CONF_HOST]), data=self.user_input) else: return self.async_show_form( step_id="user", @@ -64,13 +62,14 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Flo vol.Required("hcho", default=False): bool, } ), - errors=errors, + errors={}, ) return self.async_show_form( step_id="user", data_schema=vol.Schema( { + vol.Required(CONF_ID, default=DEFAULT_ID): str, vol.Required(CONF_HOST, default=DEFAULT_HOST): str, vol.Required(CONF_PORT, default=DEFAULT_PORT): int, vol.Required(CONF_GW, default=DEFAULT_GW): vol.In(GW_LIST), @@ -78,7 +77,7 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Flo vol.Required(CONF_SENSORS, default=True): bool, } ), - errors=errors, + errors={}, ) @staticmethod @@ -96,7 +95,7 @@ def __init__(self, config_entry: config_entries.ConfigEntry) -> None: self.config_entry = config_entry self._config_data = [] hass: HomeAssistant = GetHass.get_hash() - self._climates = list(map(lambda state: state.alias, Service.get_aircons())) + self._climates = list(map(lambda state: state.alias, Service.get_aircons(config_entry.entry_id))) sensors = hass.states.async_all("sensor") self._sensors_temp = { None: 'None', @@ -144,6 +143,7 @@ async def async_step_adjust_config(self, user_input: dict[str, Any] | None = Non step_id="adjust_config", data_schema=vol.Schema( { + vol.Required(CONF_ID, default=DEFAULT_ID): str, vol.Required(CONF_HOST, default=self.config_entry.data[CONF_HOST]): str, vol.Required(CONF_PORT, default=self.config_entry.data[CONF_PORT]): int, vol.Required(CONF_GW, default=self.config_entry.data[CONF_GW]): vol.In(GW_LIST), diff --git a/custom_components/ds_air/const.py b/custom_components/ds_air/const.py index 87e9218..a0c5c4e 100644 --- a/custom_components/ds_air/const.py +++ b/custom_components/ds_air/const.py @@ -6,6 +6,8 @@ DOMAIN = "ds_air" CONF_GW = "gw" +CONF_ID = "eid" +DEFAULT_ID = "gw1" DEFAULT_HOST = "192.168.1." DEFAULT_PORT = 8008 DEFAULT_GW = "DTA117C611" diff --git a/custom_components/ds_air/ds_air_service/dao.py b/custom_components/ds_air/ds_air_service/dao.py index 38d6350..03b5ff6 100644 --- a/custom_components/ds_air/ds_air_service/dao.py +++ b/custom_components/ds_air/ds_air_service/dao.py @@ -12,10 +12,11 @@ def __init__(self): self.room_id: int = 0 self.unit_id: int = 0 self.mac: str = "" + self.instance_id: str = "" @property def unique_id(self): - return "daikin_%d_%d" % (self.room_id, self.unit_id) + return "daikin_%s_%d_%d" % (self.instance_id, self.room_id, self.unit_id) def _nothing(): diff --git a/custom_components/ds_air/ds_air_service/decoder.py b/custom_components/ds_air/ds_air_service/decoder.py index a7ef310..69a6873 100644 --- a/custom_components/ds_air/ds_air_service/decoder.py +++ b/custom_components/ds_air/ds_air_service/decoder.py @@ -11,7 +11,7 @@ AirConQueryStatusParam, Sensor2InfoParam -def decoder(b): +def decoder(b, instance_id): if b[0] != 2: return None, None @@ -22,10 +22,10 @@ def decoder(b): else: return None, None - return result_factory(struct.unpack(' 2: @@ -468,8 +472,8 @@ def load_bytes(self, b): def do(self): from .service import Service Service.set_rooms(self.rooms) - Service.send_msg(AirConRecommendedIndoorTempParam()) - Service.set_sensors(self.sensors) + Service.send_msg(self._instance_id, AirConRecommendedIndoorTempParam()) + Service.set_sensors(self._instance_id, self.sensors) aircons = [] new_aircons = [] @@ -487,15 +491,15 @@ def do(self): p = AirConCapabilityQueryParam() p.aircons = aircons p.target = EnumDevice.AIRCON - Service.send_msg(p) + Service.send_msg(self._instance_id, p) p = AirConCapabilityQueryParam() p.aircons = new_aircons p.target = EnumDevice.NEWAIRCON - Service.send_msg(p) + Service.send_msg(self._instance_id, p) p = AirConCapabilityQueryParam() p.aircons = bathrooms p.target = EnumDevice.BATHROOM - Service.send_msg(p) + Service.send_msg(self._instance_id, p) @property def count(self): @@ -531,9 +535,10 @@ def load_bytes(self, b): class HandShakeResult(BaseResult): - def __init__(self, cmd_id: int, target: EnumDevice): + def __init__(self, cmd_id: int, target: EnumDevice, instance_id: str): BaseResult.__init__(self, cmd_id, target, EnumCmdType.SYS_HAND_SHAKE) self._time: str = '' + self._instance_id: str = instance_id def load_bytes(self, b): d = Decode(b) @@ -543,8 +548,8 @@ def do(self): p = GetRoomInfoParam() p.room_ids.append(0xffff) from .service import Service - Service.send_msg(p) - Service.send_msg(Sensor2InfoParam()) + Service.send_msg(self._instance_id, p) + Service.send_msg(self._instance_id, Sensor2InfoParam()) class GetGWInfoResult(BaseResult): @@ -576,11 +581,12 @@ def load_bytes(self, b): class AirConStatusChangedResult(BaseResult): - def __init__(self, cmd_id: int, target: EnumDevice): + def __init__(self, cmd_id: int, target: EnumDevice, instance_id: str): BaseResult.__init__(self, cmd_id, target, EnumCmdType.STATUS_CHANGED) self._room = 0 # type: int self._unit = 0 # type: int self._status = AirConStatus() # type: AirConStatus + self._instance_id = instance_id def load_bytes(self, b): d = Decode(b) @@ -606,11 +612,11 @@ def load_bytes(self, b): def do(self): from .service import Service - Service.update_aircon(self.target, self._room, self._unit, status=self._status) + Service.update_aircon(self._instance_id, self.target, self._room, self._unit, status=self._status) class AirConQueryStatusResult(BaseResult): - def __init__(self, cmd_id: int, target: EnumDevice): + def __init__(self, cmd_id: int, target: EnumDevice, instance_id: str): BaseResult.__init__(self, cmd_id, target, EnumCmdType.QUERY_STATUS) self.unit = 0 self.room = 0 @@ -627,6 +633,7 @@ def __init__(self, cmd_id: int, target: EnumDevice): self.fresh_air_allow = False self.fresh_air_humidification = FreshAirHumidification.OFF self.three_d_fresh = ThreeDFresh.CLOSE + self._instance_id = instance_id def load_bytes(self, b): d = Decode(b) @@ -682,7 +689,7 @@ def do(self): from .service import Service status = AirConStatus(self.current_temp, self.setted_temp, self.switch, self.air_flow, self.breathe, self.fan_direction1, self.fan_direction2, self.humidity, self.mode) - Service.set_aircon_status(self.target, self.room, self.unit, status) + Service.set_aircon_status(self._instance_id, self.target, self.room, self.unit, status) class AirConRecommendedIndoorTempResult(BaseResult): @@ -706,9 +713,10 @@ def outdoor_temp(self): class AirConCapabilityQueryResult(BaseResult): - def __init__(self, cmd_id: int, target: EnumDevice): + def __init__(self, cmd_id: int, target: EnumDevice, instance_id: str): BaseResult.__init__(self, cmd_id, target, EnumCmdType.AIR_CAPABILITY_QUERY) self._air_cons: typing.List[AirCon] = [] + self._instance_id = instance_id def load_bytes(self, b): d = Decode(b) @@ -763,15 +771,15 @@ def do(self): if Service.is_ready(): if len(self._air_cons): for i in self._air_cons: - Service.update_aircon(get_device_by_aircon(i), i.room_id, i.unit_id, aircon=i) + Service.update_aircon(self._instance_id, get_device_by_aircon(i), i.room_id, i.unit_id, aircon=i) else: for i in self._air_cons: p = AirConQueryStatusParam() p.target = self.target p.device = i from .service import Service - Service.send_msg(p) - Service.set_device(self.target, self._air_cons) + Service.send_msg(self._instance_id, p) + Service.set_device(self._instance_id, self.target, self._air_cons) @property def aircons(self): diff --git a/custom_components/ds_air/ds_air_service/service.py b/custom_components/ds_air/ds_air_service/service.py index fa38198..46661d1 100644 --- a/custom_components/ds_air/ds_air_service/service.py +++ b/custom_components/ds_air/ds_air_service/service.py @@ -20,9 +20,10 @@ def _log(s: str): class SocketClient: - def __init__(self, host: str, port: int): + def __init__(self, host: str, port: int, instance_id: str): self._host = host self._port = port + self._instance_id = instance_id self._locker = Lock() self._s = None while not self.do_connect(): @@ -80,7 +81,7 @@ def recv(self) -> (typing.List[BaseResult], bytes): _log("recv hex: 0x"+data.hex()) while data: try: - r, b = decoder(data) + r, b = decoder(data, self._instance_id) res.append(r) data = b except Exception as e: @@ -127,55 +128,55 @@ def run(self) -> None: time.sleep(30) cnt = 0 while self._running: - Service.send_msg(HeartbeatParam()) + for instance_id in Service._socket_clients: + Service._socket_clients[instance_id].send(HeartbeatParam()) + if cnt % Service.get_scan_interval() == 0: + _log("poll_status") + cnt = 0 + Service.poll_status(instance_id) cnt += 1 - if cnt == Service.get_scan_interval(): - _log("poll_status") - cnt = 0 - Service.poll_status() time.sleep(60) class Service: - _socket_client = None # type: SocketClient - _rooms = None # type: typing.List[Room] - _aircons = None # type: typing.List[AirCon] - _new_aircons = None # type: typing.List[AirCon] - _bathrooms = None # type: typing.List[AirCon] + _socket_clients: typing.Dict[str, SocketClient] = {} + _rooms: typing.List[Room] = None + _aircons: typing.Dict[str, typing.Dict[int, typing.List[AirCon]]] = {} + _new_aircons: typing.Dict[str, typing.Dict[int, typing.List[AirCon]]] = {} + _bathrooms: typing.Dict[str, typing.Dict[int, typing.List[AirCon]]] = {} _ready = False # type: bool _none_stat_dev_cnt = 0 # type: int - _status_hook = [] # type: typing.List[(AirCon, typing.Callable)] - _sensor_hook = [] # type: typing.List[(str, typing.Callable)] - _heartbeat_thread = None - _sensors = [] # type: typing.List[Sensor] + _status_hook: typing.Dict[str, typing.List[typing.Tuple[AirCon, typing.Callable]]] = {} + _sensor_hook: typing.Dict[str, typing.List[typing.Tuple[str, typing.Callable]]] = {} + _heartbeat_thread = HeartBeatThread() + _sensors: typing.Dict[str, typing.List[Sensor]] = None _scan_interval = 5 # type: int @staticmethod - def init(host: str, port: int, scan_interval: int): - if Service._ready: - return + def init(instance_id: str, host: str, port: int, scan_interval: int): + if instance_id in Service._socket_clients: + raise ValueError(f"Instance {instance_id} already initialized") Service._scan_interval = scan_interval - Service._socket_client = SocketClient(host, port) - Service._socket_client.send(HandShakeParam()) - Service._heartbeat_thread = HeartBeatThread() + Service._socket_clients[instance_id] = SocketClient(host, port, instance_id) + Service._socket_clients[instance_id].send(HandShakeParam()) Service._heartbeat_thread.start() - while Service._rooms is None or Service._aircons is None \ - or Service._new_aircons is None or Service._bathrooms is None: + while Service._rooms is None or Service._aircons is {} \ + or Service._new_aircons is {} or Service._bathrooms is {}: time.sleep(1) - for i in Service._aircons: + for i in Service._aircons.get(instance_id, {}).get(EnumDevice.AIRCON.value, []): for j in Service._rooms: if i.room_id == j.id: i.alias = j.alias if i.unit_id: i.alias += str(i.unit_id) - for i in Service._new_aircons: + for i in Service._new_aircons.get(instance_id, {}).get(EnumDevice.NEWAIRCON.value, []): for j in Service._rooms: if i.room_id == j.id: i.alias = j.alias if i.unit_id: i.alias += str(i.unit_id) - for i in Service._bathrooms: + for i in Service._bathrooms.get(instance_id, {}).get(EnumDevice.BATHROOM.value, []): for j in Service._rooms: if i.room_id == j.id: i.alias = j.alias @@ -184,45 +185,49 @@ def init(host: str, port: int, scan_interval: int): Service._ready = True @staticmethod - def destroy(): - if Service._ready: - Service._heartbeat_thread.terminate() - Service._socket_client.destroy() - Service._socket_client = None - Service._rooms = None - Service._aircons = None - Service._new_aircons = None - Service._bathrooms = None + def destroy(instance_id: str): + if instance_id in Service._socket_clients: + Service._socket_clients[instance_id].destroy() + del Service._socket_clients[instance_id] + if len(Service._socket_clients) <= 0: + Service._heartbeat_thread.terminate() + #Service._rooms = None + Service._aircons.pop(instance_id) + Service._new_aircons.pop(instance_id) + Service._bathrooms.pop(instance_id) Service._none_stat_dev_cnt = 0 - Service._status_hook = [] - Service._sensor_hook = [] - Service._heartbeat_thread = None - Service._sensors = [] + Service._status_hook = {} + Service._sensor_hook = {} + Service._sensors.pop(instance_id) Service._ready = False @staticmethod - def get_aircons(): + def get_aircons(instance_id: str) -> typing.List[AirCon]: aircons = [] - if Service._new_aircons is not None: - aircons += Service._new_aircons - if Service._aircons is not None: - aircons += Service._aircons - if Service._bathrooms is not None: - aircons += Service._bathrooms + for device in Service._new_aircons.get(instance_id, {}).values(): + aircons.extend(device) + for device in Service._aircons.get(instance_id, {}).values(): + aircons.extend(device) + for device in Service._bathrooms.get(instance_id, {}).values(): + aircons.extend(device) return aircons @staticmethod - def control(aircon: AirCon, status: AirConStatus): + def control(instance_id: str, aircon: AirCon, status: AirConStatus): p = AirConControlParam(aircon, status) - Service.send_msg(p) + Service.send_msg(instance_id, p) @staticmethod - def register_status_hook(device: AirCon, hook: typing.Callable): - Service._status_hook.append((device, hook)) + def register_status_hook(instance_id: str, device: AirCon, hook: typing.Callable): + if instance_id not in Service._status_hook: + Service._status_hook[instance_id] = [] + Service._status_hook[instance_id].append((device, hook)) @staticmethod - def register_sensor_hook(unique_id: str, hook: typing.Callable): - Service._sensor_hook.append((unique_id, hook)) + def register_sensor_hook(instance_id: str, unique_id: str, hook: typing.Callable): + if instance_id not in Service._sensor_hook: + Service._sensor_hook[instance_id] = [] + Service._sensor_hook[instance_id].append((unique_id, hook)) # ----split line---- above for component, below for inner call @@ -231,9 +236,12 @@ def is_ready() -> bool: return Service._ready @staticmethod - def send_msg(p: Param): + def send_msg(instance_id: str, p: Param): """send msg to climate gateway""" - Service._socket_client.send(p) + if instance_id in Service._socket_clients: + Service._socket_clients[instance_id].send(p) + else: + raise ValueError(f"Instance {instance_id} not initialized") @staticmethod def get_rooms(): @@ -244,35 +252,43 @@ def set_rooms(v: typing.List[Room]): Service._rooms = v @staticmethod - def get_sensors(): - return Service._sensors + def get_sensors(instance_id: str) -> typing.List[Sensor]: + return Service._sensors[instance_id] @staticmethod - def set_sensors(sensors): - Service._sensors = sensors + def set_sensors(instance_id: str, sensors): + if Service._sensors is None: + Service._sensors = {} + Service._sensors[instance_id] = sensors @staticmethod - def set_device(t: EnumDevice, v: typing.List[AirCon]): + def set_device(instance_id: str, t: EnumDevice, v: typing.List[AirCon]): Service._none_stat_dev_cnt += len(v) if t == EnumDevice.AIRCON: - Service._aircons = v + if instance_id not in Service._aircons: + Service._aircons[instance_id] = {} + Service._aircons[instance_id][t.value] = v elif t == EnumDevice.NEWAIRCON: - Service._new_aircons = v + if instance_id not in Service._new_aircons: + Service._new_aircons[instance_id] = {} + Service._new_aircons[instance_id][t.value] = v else: - Service._bathrooms = v + if instance_id not in Service._bathrooms: + Service._bathrooms[instance_id] = {} + Service._bathrooms[instance_id][t.value] = v @staticmethod - def set_aircon_status(target: EnumDevice, room: int, unit: int, status: AirConStatus): + def set_aircon_status(instance_id: str, target: EnumDevice, room: int, unit: int, status: AirConStatus): if Service._ready: - Service.update_aircon(target, room, unit, status=status) + Service.update_aircon(instance_id, target, room, unit, status=status) else: li = [] if target == EnumDevice.AIRCON: - li = Service._aircons + li = Service._aircons.get(instance_id, {}).get(target.value, []) elif target == EnumDevice.NEWAIRCON: - li = Service._new_aircons + li = Service._new_aircons.get(instance_id, {}).get(target.value, []) elif target == EnumDevice.BATHROOM: - li = Service._bathrooms + li = Service._bathrooms.get(instance_id, {}).get(target.value, []) for i in li: if i.unit_id == unit and i.room_id == room: i.status = status @@ -280,14 +296,14 @@ def set_aircon_status(target: EnumDevice, room: int, unit: int, status: AirConSt break @staticmethod - def set_sensors_status(sensors: typing.List[Sensor]): + def set_sensors_status(instance_id: str, sensors: typing.List[Sensor]): for new_sensor in sensors: - for sensor in Service._sensors: + for sensor in Service._sensors.get(instance_id, {}): if sensor.unique_id == new_sensor.unique_id: for attr in STATUS_ATTR: setattr(sensor, attr, getattr(new_sensor, attr)) break - for item in Service._sensor_hook: + for item in Service._sensor_hook.get(instance_id, []): unique_id, func = item if new_sensor.unique_id == unique_id: try: @@ -296,18 +312,31 @@ def set_sensors_status(sensors: typing.List[Sensor]): _log(str(e)) @staticmethod - def poll_status(): - for i in Service._new_aircons: - p = AirConQueryStatusParam() - p.target = EnumDevice.NEWAIRCON - p.device = i - Service.send_msg(p) + def poll_status(instance_id: str): + if instance_id in Service._new_aircons: + for i in Service._new_aircons[instance_id]: + p = AirConQueryStatusParam() + p.target = EnumDevice.NEWAIRCON + p.device = i + Service.send_msg(instance_id, p) + if instance_id in Service._aircons: + for i in Service._aircons[instance_id]: + p = AirConQueryStatusParam() + p.target = EnumDevice.AIRCON + p.device = i + Service.send_msg(instance_id, p) + if instance_id in Service._bathrooms: + for i in Service._bathrooms[instance_id]: + p = AirConQueryStatusParam() + p.target = EnumDevice.BATHROOM + p.device = i + Service.send_msg(instance_id, p) p = Sensor2InfoParam() - Service.send_msg(p) + Service.send_msg(instance_id, p) @staticmethod - def update_aircon(target: EnumDevice, room: int, unit: int, **kwargs): - li = Service._status_hook + def update_aircon(instance_id: str, target: EnumDevice, room: int, unit: int, **kwargs): + li = Service._status_hook.get(instance_id, []) for item in li: i, func = item if i.unit_id == unit and i.room_id == room and get_device_by_aircon(i) == target: diff --git a/custom_components/ds_air/sensor.py b/custom_components/ds_air/sensor.py index d211b98..9a1e7ff 100644 --- a/custom_components/ds_air/sensor.py +++ b/custom_components/ds_air/sensor.py @@ -4,33 +4,35 @@ from homeassistant.components.sensor import SensorEntity, SensorStateClass from homeassistant.helpers.entity import DeviceInfo -from .const import DOMAIN, SENSOR_TYPES +from .const import DOMAIN, SENSOR_TYPES, CONF_ID from .ds_air_service.dao import Sensor, UNINITIALIZED_VALUE from .ds_air_service.service import Service async def async_setup_entry(hass, config_entry, async_add_entities): """Perform the setup for Daikin devices.""" + instance_id = config_entry.data[CONF_ID] # Use CONF_ID as the instance ID entities = [] - for device in Service.get_sensors(): + for device in Service.get_sensors(instance_id): # Fetch sensors based on instance ID for key in SENSOR_TYPES: if config_entry.data.get(key): - entities.append(DsSensor(device, key)) + entities.append(DsSensor(device, key, instance_id)) # Pass instance ID to DsSensor async_add_entities(entities) class DsSensor(SensorEntity): """Representation of a DaikinSensor.""" - def __init__(self, device: Sensor, data_key): + def __init__(self, device: Sensor, data_key, instance_id: str): """Initialize the DaikinSensor.""" self._data_key = data_key self._name = device.alias self._unique_id = device.unique_id self._is_available = False self._state = 0 + self._instance_id = instance_id # Store instance ID self.parse_data(device, True) - Service.register_sensor_hook(device.unique_id, self.parse_data) + Service.register_sensor_hook(device.unique_id, self.parse_data) # Register hook with device ID @property def name(self): @@ -38,7 +40,7 @@ def name(self): @property def unique_id(self): - return "%s_%s" % (self._data_key, self._unique_id) + return "daikin_%s_%s" % (self._data_key, self._unique_id) @property def device_info(self) -> Optional[DeviceInfo]: diff --git a/custom_components/ds_air/translations/en.json b/custom_components/ds_air/translations/en.json index 2f4b115..9803a1f 100644 --- a/custom_components/ds_air/translations/en.json +++ b/custom_components/ds_air/translations/en.json @@ -5,6 +5,7 @@ "title": "DS-AIR", "description": "Support DTA117B611/DTA117C611", "data": { + "eid": "Entry ID", "host": "Host", "port": "Port", "gw": "Gateway model", @@ -40,6 +41,7 @@ "title": "Adjust config", "description": "", "data": { + "eid": "Entry ID", "host": "Gateway IP", "port": "Gateway Port", "gw": "Gateway model", diff --git a/custom_components/ds_air/translations/zh-Hans.json b/custom_components/ds_air/translations/zh-Hans.json index 96c2a03..b3ef0a2 100644 --- a/custom_components/ds_air/translations/zh-Hans.json +++ b/custom_components/ds_air/translations/zh-Hans.json @@ -5,6 +5,7 @@ "title": "金制空气", "description": "本插件支持大金DTA117B611/DTA117C611两款空调网关", "data": { + "eid": "网关ID", "host": "网关IP", "port": "网关端口", "gw": "网关型号", @@ -40,6 +41,7 @@ "title": "修改设置", "description": "", "data": { + "eid": "网关ID", "host": "网关IP", "port": "网关端口", "gw": "网关型号",