diff --git a/homeassistant/components/mysensors/__init__.py b/homeassistant/components/mysensors/__init__.py index 0a10b8eeba7..9d937aa88a3 100644 --- a/homeassistant/components/mysensors/__init__.py +++ b/homeassistant/components/mysensors/__init__.py @@ -168,7 +168,6 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool Every instance has a connection to exactly one Gateway. """ - _LOGGER.debug("async_setup_entry: %s (id: %s)", entry.title, entry.entry_id) gateway = await setup_gateway(hass, entry) if not gateway: @@ -283,7 +282,7 @@ def setup_mysensors_platform( s_type = gateway.const.Presentation(child.type).name device_class_copy = device_class[s_type] - args_copy = (*device_args, gateway, node_id, child_id, value_type) + args_copy = (*device_args, gateway_id, gateway, node_id, child_id, value_type) devices[dev_id] = device_class_copy(*args_copy) new_devices.append(devices[dev_id]) if new_devices: diff --git a/homeassistant/components/mysensors/climate.py b/homeassistant/components/mysensors/climate.py index 61457d0a2b3..d8cbc404767 100644 --- a/homeassistant/components/mysensors/climate.py +++ b/homeassistant/components/mysensors/climate.py @@ -87,12 +87,12 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): @property def assumed_state(self): """Return True if unable to access real state of entity.""" - return self.gateway.optimistic + return False @property def temperature_unit(self): """Return the unit of measurement.""" - return TEMP_CELSIUS if self.gateway.metric else TEMP_FAHRENHEIT + return TEMP_CELSIUS if self.hass.config.units.is_metric else TEMP_FAHRENHEIT @property def current_temperature(self): @@ -181,7 +181,7 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): self.gateway.set_child_value( self.node_id, self.child_id, value_type, value, ack=1 ) - if self.gateway.optimistic: + if self.om: # Optimistically assume that device has changed state self._values[value_type] = value self.async_write_ha_state() @@ -192,7 +192,7 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_HVAC_SPEED, fan_mode, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that device has changed state self._values[set_req.V_HVAC_SPEED] = fan_mode self.async_write_ha_state() @@ -206,7 +206,7 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): DICT_HA_TO_MYS[hvac_mode], ack=1, ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that device has changed state self._values[self.value_type] = hvac_mode self.async_write_ha_state() diff --git a/homeassistant/components/mysensors/const.py b/homeassistant/components/mysensors/const.py index 857eb43ce2a..29dfd4501d7 100644 --- a/homeassistant/components/mysensors/const.py +++ b/homeassistant/components/mysensors/const.py @@ -3,6 +3,7 @@ from collections import defaultdict from typing import Dict, List, Literal, Set, Tuple ATTR_DEVICES: str = "devices" +ATTR_GATEWAY_ID: str = "gateway_id" CONF_BAUD_RATE: str = "baud_rate" CONF_DEVICE: str = "device" diff --git a/homeassistant/components/mysensors/cover.py b/homeassistant/components/mysensors/cover.py index 9e3ba156ed1..b62ee35287c 100644 --- a/homeassistant/components/mysensors/cover.py +++ b/homeassistant/components/mysensors/cover.py @@ -31,7 +31,7 @@ async def async_setup_entry( await on_unload( hass, - config_entry, + config_entry.entry_id, async_dispatcher_connect( hass, MYSENSORS_DISCOVERY.format(config_entry.entry_id, DOMAIN), @@ -46,7 +46,7 @@ class MySensorsCover(mysensors.device.MySensorsEntity, CoverEntity): @property def assumed_state(self): """Return True if unable to access real state of entity.""" - return self.gateway.optimistic + return False @property def is_closed(self): @@ -71,7 +71,7 @@ class MySensorsCover(mysensors.device.MySensorsEntity, CoverEntity): self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_UP, 1, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that cover has changed state. if set_req.V_DIMMER in self._values: self._values[set_req.V_DIMMER] = 100 @@ -85,7 +85,7 @@ class MySensorsCover(mysensors.device.MySensorsEntity, CoverEntity): self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_DOWN, 1, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that cover has changed state. if set_req.V_DIMMER in self._values: self._values[set_req.V_DIMMER] = 0 @@ -100,7 +100,7 @@ class MySensorsCover(mysensors.device.MySensorsEntity, CoverEntity): self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_DIMMER, position, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that cover has changed state. self._values[set_req.V_DIMMER] = position self.async_write_ha_state() diff --git a/homeassistant/components/mysensors/device.py b/homeassistant/components/mysensors/device.py index 907612a7996..516953d85c7 100644 --- a/homeassistant/components/mysensors/device.py +++ b/homeassistant/components/mysensors/device.py @@ -13,11 +13,13 @@ from homeassistant.helpers.entity import Entity from .const import ( CHILD_CALLBACK, + CONF_DEVICE, DOMAIN, NODE_CALLBACK, PLATFORM_TYPES, UPDATE_DELAY, DevId, + GatewayId, ) _LOGGER = logging.getLogger(__name__) @@ -33,8 +35,16 @@ MYSENSORS_PLATFORM_DEVICES = "mysensors_devices_{}" class MySensorsDevice: """Representation of a MySensors device.""" - def __init__(self, gateway, node_id, child_id, value_type): + def __init__( + self, + gateway_id: GatewayId, + gateway: BaseAsyncGateway, + node_id: int, + child_id: int, + value_type: int, + ): """Set up the MySensors device.""" + self.gateway_id: GatewayId = gateway_id self.gateway: BaseAsyncGateway = gateway self.node_id: int = node_id self.child_id: int = child_id @@ -72,11 +82,6 @@ class MySensorsDevice: "deleted %s from platform %s", self.dev_id, platform ) - @property - def gateway_id(self) -> str: - """Return the id of the gateway that this device belongs to.""" - return self.gateway.entry_id - @property def _mysensors_sensor(self) -> Sensor: return self.gateway.sensors[self.node_id] @@ -131,9 +136,10 @@ class MySensorsDevice: ATTR_HEARTBEAT: node.heartbeat, ATTR_CHILD_ID: self.child_id, ATTR_DESCRIPTION: child.description, - ATTR_DEVICE: self.gateway.device, ATTR_NODE_ID: self.node_id, } + if hasattr(self, "platform"): + attr[ATTR_DEVICE] = self.platform.config_entry.data[CONF_DEVICE] set_req = self.gateway.const.SetReq diff --git a/homeassistant/components/mysensors/device_tracker.py b/homeassistant/components/mysensors/device_tracker.py index 06fa384f7d7..a990b08ed73 100644 --- a/homeassistant/components/mysensors/device_tracker.py +++ b/homeassistant/components/mysensors/device_tracker.py @@ -2,7 +2,7 @@ from homeassistant.components import mysensors from homeassistant.components.device_tracker import DOMAIN from homeassistant.components.mysensors import DevId, on_unload -from homeassistant.components.mysensors.const import GatewayId +from homeassistant.components.mysensors.const import ATTR_GATEWAY_ID, GatewayId from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util import slugify @@ -14,6 +14,7 @@ async def async_setup_scanner( """Set up the MySensors device scanner.""" new_devices = mysensors.setup_mysensors_platform( hass, + discovery_info[ATTR_GATEWAY_ID], DOMAIN, discovery_info, MySensorsDeviceScanner, @@ -23,7 +24,7 @@ async def async_setup_scanner( return False for device in new_devices: - gateway_id: GatewayId = device.gateway.entry_id + gateway_id: GatewayId = discovery_info[ATTR_GATEWAY_ID] dev_id: DevId = (gateway_id, device.node_id, device.child_id, device.value_type) await on_unload( hass, diff --git a/homeassistant/components/mysensors/gateway.py b/homeassistant/components/mysensors/gateway.py index dc79d2a0ab6..e5499c8eaf9 100644 --- a/homeassistant/components/mysensors/gateway.py +++ b/homeassistant/components/mysensors/gateway.py @@ -211,11 +211,6 @@ async def _get_gateway( except vol.Invalid: # invalid ip address return None - # this adds extra properties to the pymysensors objects - gateway.metric = hass.config.units.is_metric - gateway.optimistic = False # old optimistic option has been deprecated, we use echos to hopefully not need it - gateway.device = device - gateway.entry_id = unique_id gateway.event_callback = _gw_callback_factory(hass, entry) if persistence: await gateway.start_persistence() @@ -230,7 +225,7 @@ async def finish_setup( discover_tasks = [] start_tasks = [] discover_tasks.append(_discover_persistent_devices(hass, hass_config, gateway)) - start_tasks.append(_gw_start(hass, gateway)) + start_tasks.append(_gw_start(hass, hass_config, gateway)) if discover_tasks: # Make sure all devices and platforms are loaded before gateway start. await asyncio.wait(discover_tasks) @@ -249,7 +244,7 @@ async def _discover_persistent_devices( continue node: Sensor = gateway.sensors[node_id] for child in node.children.values(): # child is of type ChildSensor - validated = validate_child(gateway, node_id, child) + validated = validate_child(hass_config, gateway, node_id, child) for platform, dev_ids in validated.items(): new_devices[platform].extend(dev_ids) _LOGGER.debug("discovering persistent devices: %s", new_devices) @@ -259,35 +254,36 @@ async def _discover_persistent_devices( await asyncio.wait(tasks) -async def gw_stop(hass, gateway: BaseAsyncGateway): +async def gw_stop(hass, hass_config: ConfigEntry, gateway: BaseAsyncGateway): """Stop the gateway.""" - _LOGGER.info("stopping gateway %s", gateway.entry_id) connect_task = hass.data[DOMAIN].get( - MYSENSORS_GATEWAY_START_TASK.format(gateway.entry_id), None + MYSENSORS_GATEWAY_START_TASK.format(hass_config.entry_id), None ) if connect_task is not None and not connect_task.done(): connect_task.cancel() await gateway.stop() -async def _gw_start(hass: HomeAssistantType, gateway: BaseAsyncGateway): +async def _gw_start( + hass: HomeAssistantType, hass_config: ConfigEntry, gateway: BaseAsyncGateway +): """Start the gateway.""" # Don't use hass.async_create_task to avoid holding up setup indefinitely. hass.data[DOMAIN][ - MYSENSORS_GATEWAY_START_TASK.format(gateway.entry_id) + MYSENSORS_GATEWAY_START_TASK.format(hass_config.entry_id) ] = asyncio.create_task( gateway.start() ) # store the connect task so it can be cancelled in gw_stop async def stop_this_gw(_: Event): - await gw_stop(hass, gateway) + await gw_stop(hass, hass_config, gateway) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_this_gw) - if gateway.device == "mqtt": + if hass_config.data[CONF_DEVICE] == MQTT_COMPONENT: # Gatways connected via mqtt doesn't send gateway ready message. return gateway_ready = asyncio.Future() - gateway_ready_key = MYSENSORS_GATEWAY_READY.format(gateway.entry_id) + gateway_ready_key = MYSENSORS_GATEWAY_READY.format(hass_config.entry_id) hass.data[DOMAIN][gateway_ready_key] = gateway_ready try: @@ -296,7 +292,7 @@ async def _gw_start(hass: HomeAssistantType, gateway: BaseAsyncGateway): except asyncio.TimeoutError: _LOGGER.warning( "Gateway %s not ready after %s secs so continuing with setup", - gateway.device, + hass_config.data[CONF_DEVICE], GATEWAY_READY_TIMEOUT, ) finally: diff --git a/homeassistant/components/mysensors/handler.py b/homeassistant/components/mysensors/handler.py index 2eb41b794d0..1e21cdeaf02 100644 --- a/homeassistant/components/mysensors/handler.py +++ b/homeassistant/components/mysensors/handler.py @@ -3,11 +3,12 @@ from typing import Dict, List from mysensors import Message +from homeassistant.config_entries import ConfigEntry from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util import decorator -from ...config_entries import ConfigEntry from .const import CHILD_CALLBACK, MYSENSORS_GATEWAY_READY, NODE_CALLBACK, DevId from .device import get_mysensors_devices from .helpers import discover_mysensors_platform, validate_set_msg @@ -18,7 +19,7 @@ HANDLERS = decorator.Registry() @HANDLERS.register("set") async def handle_set(hass, hass_config: ConfigEntry, msg: Message) -> None: """Handle a mysensors set message.""" - validated = validate_set_msg(msg) + validated = validate_set_msg(hass_config, msg) _handle_child_update(hass, hass_config, validated) @@ -41,19 +42,19 @@ async def handle_battery_level(hass, hass_config: ConfigEntry, msg: Message) -> @HANDLERS.register("I_HEARTBEAT_RESPONSE") async def handle_heartbeat(hass, hass_config: ConfigEntry, msg: Message) -> None: """Handle an heartbeat.""" - _handle_node_update(hass, msg) + _handle_node_update(hass, hass_config, msg) @HANDLERS.register("I_SKETCH_NAME") async def handle_sketch_name(hass, hass_config: ConfigEntry, msg: Message) -> None: """Handle an internal sketch name message.""" - _handle_node_update(hass, msg) + _handle_node_update(hass, hass_config, msg) @HANDLERS.register("I_SKETCH_VERSION") async def handle_sketch_version(hass, hass_config: ConfigEntry, msg: Message) -> None: """Handle an internal sketch version message.""" - _handle_node_update(hass, msg) + _handle_node_update(hass, hass_config, msg) @HANDLERS.register("I_GATEWAY_READY") @@ -62,7 +63,7 @@ async def handle_gateway_ready(hass, hass_config: ConfigEntry, msg: Message) -> Set asyncio future result if gateway is ready. """ - gateway_ready = hass.data.get(MYSENSORS_GATEWAY_READY.format(msg.gateway.entry_id)) + gateway_ready = hass.data.get(MYSENSORS_GATEWAY_READY.format(hass_config.entry_id)) if gateway_ready is None or gateway_ready.cancelled(): return gateway_ready.set_result(True) @@ -94,7 +95,9 @@ def _handle_child_update( @callback -def _handle_node_update(hass, msg): +def _handle_node_update( + hass: HomeAssistantType, hass_config: ConfigEntry, msg: Message +): """Handle a node update.""" - signal = NODE_CALLBACK.format(msg.gateway.entry_id, msg.node_id) + signal = NODE_CALLBACK.format(hass_config.entry_id, msg.node_id) async_dispatcher_send(hass, signal) diff --git a/homeassistant/components/mysensors/helpers.py b/homeassistant/components/mysensors/helpers.py index f7b796e8981..7576ec47ac4 100644 --- a/homeassistant/components/mysensors/helpers.py +++ b/homeassistant/components/mysensors/helpers.py @@ -17,6 +17,7 @@ from ...config_entries import ConfigEntry from ...helpers.dispatcher import async_dispatcher_send from .const import ( ATTR_DEVICES, + ATTR_GATEWAY_ID, DOMAIN, FLAT_PLATFORM_TYPES, MYSENSORS_DISCOVERY, @@ -39,7 +40,11 @@ def discover_mysensors_platform( async_dispatcher_send( hass, MYSENSORS_DISCOVERY.format(hass_config.entry_id, platform), - {ATTR_DEVICES: new_devices, CONF_NAME: DOMAIN}, + { + ATTR_DEVICES: new_devices, + CONF_NAME: DOMAIN, + ATTR_GATEWAY_ID: hass_config.entry_id, + }, ) @@ -125,12 +130,12 @@ def invalid_msg( ) -def validate_set_msg(msg: Message) -> Dict[str, List[DevId]]: +def validate_set_msg(hass_config: ConfigEntry, msg: Message) -> Dict[str, List[DevId]]: """Validate a set message.""" if not validate_node(msg.gateway, msg.node_id): return {} child = msg.gateway.sensors[msg.node_id].children[msg.child_id] - return validate_child(msg.gateway, msg.node_id, child, msg.sub_type) + return validate_child(hass_config, msg.gateway, msg.node_id, child, msg.sub_type) def validate_node(gateway: BaseAsyncGateway, node_id: int) -> bool: @@ -142,6 +147,7 @@ def validate_node(gateway: BaseAsyncGateway, node_id: int) -> bool: def validate_child( + hass_config: ConfigEntry, gateway: BaseAsyncGateway, node_id: int, child: ChildSensor, @@ -188,7 +194,12 @@ def validate_child( exc, ) continue - dev_id: DevId = (gateway.entry_id, node_id, child.id, set_req[v_name].value) + dev_id: DevId = ( + hass_config.entry_id, + node_id, + child.id, + set_req[v_name].value, + ) validated[platform].append(dev_id) return validated diff --git a/homeassistant/components/mysensors/light.py b/homeassistant/components/mysensors/light.py index c86e33a4622..864ba57e8e9 100644 --- a/homeassistant/components/mysensors/light.py +++ b/homeassistant/components/mysensors/light.py @@ -85,7 +85,7 @@ class MySensorsLight(mysensors.device.MySensorsEntity, LightEntity): @property def assumed_state(self): """Return true if unable to access real state of entity.""" - return self.gateway.optimistic + return False @property def is_on(self): @@ -102,7 +102,7 @@ class MySensorsLight(mysensors.device.MySensorsEntity, LightEntity): self.node_id, self.child_id, set_req.V_LIGHT, 1, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # optimistically assume that light has changed state self._state = True self._values[set_req.V_LIGHT] = STATE_ON @@ -124,7 +124,7 @@ class MySensorsLight(mysensors.device.MySensorsEntity, LightEntity): self.node_id, self.child_id, set_req.V_DIMMER, percent, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # optimistically assume that light has changed state self._brightness = brightness self._values[set_req.V_DIMMER] = percent @@ -157,7 +157,7 @@ class MySensorsLight(mysensors.device.MySensorsEntity, LightEntity): self.node_id, self.child_id, self.value_type, hex_color, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # optimistically assume that light has changed state self._hs = color_util.color_RGB_to_hs(*rgb) self._white = white @@ -167,7 +167,7 @@ class MySensorsLight(mysensors.device.MySensorsEntity, LightEntity): """Turn the device off.""" value_type = self.gateway.const.SetReq.V_LIGHT self.gateway.set_child_value(self.node_id, self.child_id, value_type, 0, ack=1) - if self.gateway.optimistic: + if self.assumed_state: # optimistically assume that light has changed state self._state = False self._values[value_type] = STATE_OFF @@ -210,7 +210,7 @@ class MySensorsLightDimmer(MySensorsLight): """Turn the device on.""" self._turn_on_light() self._turn_on_dimmer(**kwargs) - if self.gateway.optimistic: + if self.assumed_state: self.async_write_ha_state() async def async_update(self): @@ -236,7 +236,7 @@ class MySensorsLightRGB(MySensorsLight): self._turn_on_light() self._turn_on_dimmer(**kwargs) self._turn_on_rgb_and_w("%02x%02x%02x", **kwargs) - if self.gateway.optimistic: + if self.assumed_state: self.async_write_ha_state() async def async_update(self): @@ -263,5 +263,5 @@ class MySensorsLightRGBW(MySensorsLightRGB): self._turn_on_light() self._turn_on_dimmer(**kwargs) self._turn_on_rgb_and_w("%02x%02x%02x%02x", **kwargs) - if self.gateway.optimistic: + if self.assumed_state: self.async_write_ha_state() diff --git a/homeassistant/components/mysensors/sensor.py b/homeassistant/components/mysensors/sensor.py index e40f28ac884..a09f8af1394 100644 --- a/homeassistant/components/mysensors/sensor.py +++ b/homeassistant/components/mysensors/sensor.py @@ -127,7 +127,7 @@ class MySensorsSensor(mysensors.device.MySensorsEntity): pres = self.gateway.const.Presentation set_req = self.gateway.const.SetReq SENSORS[set_req.V_TEMP.name][0] = ( - TEMP_CELSIUS if self.gateway.metric else TEMP_FAHRENHEIT + TEMP_CELSIUS if self.hass.config.units.is_metric else TEMP_FAHRENHEIT ) sensor_type = SENSORS.get(set_req(self.value_type).name, [None, None]) if isinstance(sensor_type, dict): diff --git a/homeassistant/components/mysensors/switch.py b/homeassistant/components/mysensors/switch.py index 33c9befbede..c2e8e815763 100644 --- a/homeassistant/components/mysensors/switch.py +++ b/homeassistant/components/mysensors/switch.py @@ -99,7 +99,7 @@ class MySensorsSwitch(mysensors.device.MySensorsEntity, SwitchEntity): @property def assumed_state(self): """Return True if unable to access real state of entity.""" - return self.gateway.optimistic + return False @property def current_power_w(self): @@ -117,7 +117,7 @@ class MySensorsSwitch(mysensors.device.MySensorsEntity, SwitchEntity): self.gateway.set_child_value( self.node_id, self.child_id, self.value_type, 1, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that switch has changed state self._values[self.value_type] = STATE_ON self.async_write_ha_state() @@ -127,7 +127,7 @@ class MySensorsSwitch(mysensors.device.MySensorsEntity, SwitchEntity): self.gateway.set_child_value( self.node_id, self.child_id, self.value_type, 0, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that switch has changed state self._values[self.value_type] = STATE_OFF self.async_write_ha_state() @@ -158,7 +158,7 @@ class MySensorsIRSwitch(MySensorsSwitch): self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_LIGHT, 1, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that switch has changed state self._values[self.value_type] = self._ir_code self._values[set_req.V_LIGHT] = STATE_ON @@ -172,7 +172,7 @@ class MySensorsIRSwitch(MySensorsSwitch): self.gateway.set_child_value( self.node_id, self.child_id, set_req.V_LIGHT, 0, ack=1 ) - if self.gateway.optimistic: + if self.assumed_state: # Optimistically assume that switch has changed state self._values[set_req.V_LIGHT] = STATE_OFF self.async_write_ha_state()