async def test_disconnected(hass, caplog): """Test disconnected behavior.""" mocked_device = _create_mocked_device() entry = MockConfigEntry(domain=songpal.DOMAIN, data=CONF_DATA) entry.add_to_hass(hass) with _patch_media_player_device(mocked_device): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() async def _assert_state(): state = hass.states.get(ENTITY_ID) assert state.state == STATE_UNAVAILABLE connect_change = MagicMock() connect_change.exception = "disconnected" type(mocked_device).get_supported_methods = AsyncMock(side_effect=[ SongpalException(""), SongpalException(""), _assert_state ]) notification_callbacks = mocked_device.notification_callbacks with patch( "homeassistant.components.songpal.media_player.INITIAL_RETRY_DELAY", 0): await notification_callbacks[ConnectChange](connect_change) warning_records = [ x for x in caplog.records if x.levelno == logging.WARNING ] assert len(warning_records) == 2 assert warning_records[0].message.endswith( "Got disconnected, trying to reconnect") assert warning_records[1].message.endswith("Connection reestablished") assert not any(x.levelno == logging.ERROR for x in caplog.records)
async def __call__(self, *args, **kwargs): """Call the method with given parameters. On error this call will raise a :class:SongpalException:. If the error is reported by the device (e.g. not a problem doing the request), the exception will contain `error` attribute containing the device-reported error message. """ try: res = await self.service.call_method(self, *args, **kwargs) except Exception as ex: raise SongpalException("Unable to make a request: %s" % ex) from ex if self.debug > 1: _LOGGER.debug("got payload: %s" % res) if "error" in res: _LOGGER.debug(self) raise SongpalException( "Got an error for %s: %s" % (self.name, res["error"]), error=res["error"], ) if self.debug > 0: _LOGGER.debug("got res: %s" % pf(res)) if "result" not in res: _LOGGER.error("No result in response, how to handle? %s" % res) return res = res["result"] if len(res) > 1: _LOGGER.warning("Got a response with len > 1: %s" % res) return res elif len(res) < 1: _LOGGER.debug("Got no response, assuming success") return True return res[0]
def parse_json_types(x) -> Union[type, str, Dict[str, type]]: """Parse JSON signature. Used to parse input and output parameters.""" try: if x.endswith("*"): # TODO handle arrays properly # _LOGGER.debug("got an array %s: %s" % (self.name, x)) x = x.rstrip("*") obj = json.loads(x) obj = {x: MethodSignature.return_type(obj[x]) for x in obj} except json.JSONDecodeError as ex: try: return MethodSignature.return_type(x) except Exception: raise SongpalException("Unknown return type: %s" % x) from ex return obj
def _create_mocked_device(throw_exception=False): mocked_device = MagicMock() type(mocked_device).get_supported_methods = AsyncMock( side_effect=SongpalException("Unable to do POST request: " ) if throw_exception else None) interface_info = MagicMock() interface_info.modelName = MODEL type(mocked_device).get_interface_information = AsyncMock( return_value=interface_info) sys_info = MagicMock() sys_info.macAddr = MAC sys_info.version = SW_VERSION type(mocked_device).get_system_info = AsyncMock(return_value=sys_info) volume1 = MagicMock() volume1.maxVolume = 100 volume1.minVolume = 0 volume1.volume = 50 volume1.is_muted = False volume1.set_volume = AsyncMock() volume1.set_mute = AsyncMock() volume2 = MagicMock() volume2.maxVolume = 100 volume2.minVolume = 0 volume2.volume = 20 volume2.is_muted = True mocked_device.volume1 = volume1 type(mocked_device).get_volume_information = AsyncMock( return_value=[volume1, volume2]) power = MagicMock() power.status = True type(mocked_device).get_power = AsyncMock(return_value=power) input1 = MagicMock() input1.title = "title1" input1.uri = "uri1" input1.active = False input1.activate = AsyncMock() mocked_device.input1 = input1 input2 = MagicMock() input2.title = "title2" input2.uri = "uri2" input2.active = True type(mocked_device).get_inputs = AsyncMock(return_value=[input1, input2]) type(mocked_device).set_power = AsyncMock() type(mocked_device).set_sound_settings = AsyncMock() type(mocked_device).listen_notifications = AsyncMock() type(mocked_device).stop_listen_notifications = AsyncMock() notification_callbacks = {} mocked_device.notification_callbacks = notification_callbacks def _on_notification(name, callback): notification_callbacks[name] = callback type(mocked_device).on_notification = MagicMock( side_effect=_on_notification) type(mocked_device).clear_notification_callbacks = MagicMock() return mocked_device