async def test_config_google_home_entity_id_to_number(hass, hass_storage): """Test config adheres to the type.""" conf = Config(hass, {"type": "google_home"}, "127.0.0.1") hass_storage[DATA_KEY] = { "version": DATA_VERSION, "key": DATA_KEY, "data": {"1": "light.test2"}, } await conf.async_setup() number = conf.entity_id_to_number("light.test") assert number == "2" async_fire_time_changed(hass, utcnow() + timedelta(seconds=SAVE_DELAY)) await hass.async_block_till_done() assert hass_storage[DATA_KEY]["data"] == { "1": "light.test2", "2": "light.test", } number = conf.entity_id_to_number("light.test") assert number == "2" number = conf.entity_id_to_number("light.test2") assert number == "1" entity_id = conf.number_to_entity_id("1") assert entity_id == "light.test2"
async def test_site_cannot_update(hass, requests_mock, legacy_patchable_time): """Test we handle cannot connect error.""" # all metoffice test data encapsulated in here mock_json = json.loads(load_fixture("metoffice.json")) all_sites = json.dumps(mock_json["all_sites"]) wavertree_hourly = json.dumps(mock_json["wavertree_hourly"]) requests_mock.get("/public/data/val/wxfcs/all/json/sitelist/", text=all_sites) requests_mock.get("/public/data/val/wxfcs/all/json/354107?res=3hourly", text=wavertree_hourly) entry = MockConfigEntry( domain=DOMAIN, data=METOFFICE_CONFIG_WAVERTREE, ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() entity = hass.states.get("weather.met_office_wavertree") assert entity requests_mock.get("/public/data/val/wxfcs/all/json/354107?res=3hourly", text="") future_time = utcnow() + timedelta(minutes=20) async_fire_time_changed(hass, future_time) await hass.async_block_till_done() entity = hass.states.get("weather.met_office_wavertree") assert entity.state == STATE_UNAVAILABLE
async def test_thermostat_set_hvac_mode_off(hass, cube: MaxCube, thermostat: MaxThermostat): """Turn off thermostat.""" await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, { ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVAC_MODE_OFF }, blocking=True, ) cube.set_temperature_mode.assert_called_once_with(thermostat, OFF_TEMPERATURE, MAX_DEVICE_MODE_MANUAL) thermostat.mode = MAX_DEVICE_MODE_MANUAL thermostat.target_temperature = OFF_TEMPERATURE thermostat.valve_position = 0 async_fire_time_changed(hass, utcnow() + timedelta(minutes=5)) await hass.async_block_till_done() state = hass.states.get(ENTITY_ID) assert state.state == HVAC_MODE_OFF assert state.attributes.get(ATTR_TEMPERATURE) is None assert state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_OFF assert state.attributes.get(VALVE_POSITION) == 0 wall_state = hass.states.get(WALL_ENTITY_ID) assert wall_state.attributes.get(ATTR_HVAC_ACTION) == CURRENT_HVAC_OFF
async def test_thermostat_set_preset_comfort(hass, cube: MaxCube, thermostat: MaxThermostat): """Set preset mode to comfort.""" await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_PRESET_MODE, { ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_COMFORT }, blocking=True, ) cube.set_temperature_mode.assert_called_once_with( thermostat, thermostat.comfort_temperature, MAX_DEVICE_MODE_MANUAL) thermostat.mode = MAX_DEVICE_MODE_MANUAL thermostat.target_temperature = thermostat.comfort_temperature async_fire_time_changed(hass, utcnow() + timedelta(minutes=5)) await hass.async_block_till_done() state = hass.states.get(ENTITY_ID) assert state.state == HVAC_MODE_HEAT assert state.attributes.get( ATTR_TEMPERATURE) == thermostat.comfort_temperature assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_COMFORT
def set_blind_position(self, blind, position): self._pending_blind_positions[blind.encoded_mac] = position if self._cancel_pending_blind_position_timer is not None: self._cancel_pending_blind_position_timer() self._cancel_pending_blind_position_timer = track_point_in_utc_time( self._hass, self._set_blind_positions, utcnow() + timedelta(seconds=POSITION_BATCHING_DELAY_SEC))
async def help_test_entity_available(hass: core.HomeAssistant, domain: str, device: Dict[str, Any], entity_id: str): """Run common test to verify available property.""" await setup_platform(hass, domain, device) assert hass.states.get(entity_id).state != STATE_UNAVAILABLE with patch_bond_device_state(side_effect=AsyncIOTimeoutError()): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_UNAVAILABLE with patch_bond_device_state(return_value={}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get(entity_id).state != STATE_UNAVAILABLE
def scheduled(retry=0, untrack=None, event=None): """Call the target method. It is called directly at the first time and then called scheduled within the Hass mainloop. """ if untrack is not None: wrapper._retry_queue.remove(untrack) # pylint: disable=broad-except try: method(*args, **kwargs) except Exception as ex: if retry == self.retry_limit: raise if len(wrapper._retry_queue) >= self.queue_limit: last = wrapper._retry_queue.pop(0) if 'remove' in last: func = last['remove'] func() if 'exc' in last: _LOGGER.error( "Retry queue overflow, drop oldest entry: %s", str(last['exc'])) target = utcnow() + self.retry_delay tracking = {'target': target} remove = track_point_in_utc_time(self.hass, partial(scheduled, retry + 1, tracking), target) tracking['remove'] = remove tracking["exc"] = ex wrapper._retry_queue.append(tracking)
def _update(self): """Get the latest data from DataPoint.""" if self._site is None: _LOGGER.error("No Met Office sites held, check logs for problems") return try: forecast_3hourly = self._datapoint.get_forecast_for_site( self._site.id, MODE_3HOURLY) forecast_daily = self._datapoint.get_forecast_for_site( self._site.id, MODE_DAILY) except (ValueError, datapoint.exceptions.APIException) as err: _LOGGER.error("Check Met Office connection: %s", err.args) self.forecast_3hourly = None self.forecast_daily = None self.now_3hourly = None self.now_daily = None else: self.now_3hourly = forecast_3hourly.now() self.now_daily = forecast_daily.now() time_now = utcnow() self.forecast_3hourly = [ timestep for day in forecast_3hourly.days for timestep in day.timesteps if timestep.date > time_now ] self.forecast_daily = [ timestep for day in forecast_daily.days for timestep in day.timesteps if timestep.date > time_now ]
def __init__(self, device, login): # pylint: disable=unused-argument """Initialize the Alexa device.""" from alexapy import AlexaAPI # Class info self._login = login self.alexa_api = AlexaAPI(self, login) self.auth = None self.alexa_api_session = login.session self.account = hide_email(login.email) # Logged in info self._authenticated = None self._can_access_prime_music = None self._customer_email = None self._customer_id = None self._customer_name = None # Device info self._device_name = None self._device_serial_number = None self._device_type = None self._device_family = None self._device_owner_customer_id = None self._software_version = None self._available = None self._capabilities = [] self._cluster_members = [] self._locale = None # Media self._session = None self._media_duration = None self._media_image_url = None self._media_title = None self._media_pos = None self._media_album_name = None self._media_artist = None self._media_player_state = None self._media_is_muted = None self._media_vol_level = None self._previous_volume = None self._source = None self._source_list = [] self._shuffle = None self._repeat = None self._playing_parent = None # Last Device self._last_called = None self._last_called_timestamp = None # Do not Disturb state self._dnd = None # Polling state self._should_poll = True self._last_update = util.utcnow() self._listener = None self._bluetooth_state = None self._app_device_list = None self._parent_clusters = None self._timezone = None
def update(self): """Get the latest details on a media player. Because media players spend the majority of time idle, an adaptive update should be used to avoid flooding Amazon focusing on known play states. An initial version included an update_devices call on every update. However, this quickly floods the network for every new device added. This should only call refresh() to call the AlexaAPI. """ if (self._device is None or self.entity_id is None): # Device has not initialized yet return email = self._login.email device = (self.hass.data[DATA_ALEXAMEDIA] ['accounts'] [email] ['devices'] ['media_player'] [self.unique_id]) self.refresh(device, # pylint: disable=unexpected-keyword-arg no_throttle=True) if (self.state in [STATE_PLAYING] and # only enable polling if websocket not connected (not self.hass.data[DATA_ALEXAMEDIA] ['accounts'][email]['websocket'])): self._should_poll = False # disable polling since manual update if(self._last_update == 0 or util.dt.as_timestamp(util.utcnow()) - util.dt.as_timestamp(self._last_update) > PLAY_SCAN_INTERVAL): _LOGGER.debug("%s playing; scheduling update in %s seconds", self.name, PLAY_SCAN_INTERVAL) call_later(self.hass, PLAY_SCAN_INTERVAL, lambda _: self.schedule_update_ha_state(force_refresh=True)) elif self._should_poll: # Not playing, one last poll self._should_poll = False if not (self.hass.data[DATA_ALEXAMEDIA] ['accounts'][email]['websocket']): _LOGGER.debug("Disabling polling and scheduling last update in" " 300 seconds for %s", self.name) call_later(self.hass, 300, lambda _: self.schedule_update_ha_state(force_refresh=True)) else: _LOGGER.debug("Disabling polling for %s", self.name) self._last_update = util.utcnow() self.schedule_update_ha_state()
async def test_bpup_goes_offline_and_recovers_same_entity(hass: core.HomeAssistant): """Test that push updates fail and we fallback to polling and then bpup recovers. The BPUP recovery is triggered by an update for the entity and we do not fallback to polling because state is in sync. """ bpup_subs = BPUPSubscriptions() with patch( "homeassistant.components.bond.BPUPSubscriptions", return_value=bpup_subs, ): await setup_platform( hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id" ) bpup_subs.notify( { "s": 200, "t": "bond/test-device-id/update", "b": {"power": 1, "speed": 3, "direction": 0}, } ) await hass.async_block_till_done() assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 100 bpup_subs.notify( { "s": 200, "t": "bond/test-device-id/update", "b": {"power": 1, "speed": 1, "direction": 0}, } ) await hass.async_block_till_done() assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 33 bpup_subs.last_message_time = 0 with patch_bond_device_state(side_effect=asyncio.TimeoutError): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() assert hass.states.get("fan.name_1").state == STATE_UNAVAILABLE # Ensure we do not poll to get the state # since bpup has recovered and we know we # are back in sync with patch_bond_device_state(side_effect=Exception): bpup_subs.notify( { "s": 200, "t": "bond/test-device-id/update", "b": {"power": 1, "speed": 2, "direction": 0}, } ) await hass.async_block_till_done() state = hass.states.get("fan.name_1") assert state.state == STATE_ON assert state.attributes[fan.ATTR_PERCENTAGE] == 66
async def test_update_reports_closed_cover(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports cover is closed.""" await setup_platform(hass, COVER_DOMAIN, shades("name-1")) with patch_bond_device_state(return_value={"open": 0}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("cover.name_1").state == "closed"
async def _goto_future(hass, future=None): """Move to future.""" if not future: future = utcnow() + datetime.timedelta(minutes=10) with patch("homeassistant.util.utcnow", return_value=future): async_fire_time_changed(hass, future) await hass.async_block_till_done() async_fire_time_changed(hass, future) await hass.async_block_till_done()
async def test_update_reports_down_light_is_off(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports the down light is off.""" await setup_platform(hass, LIGHT_DOMAIN, down_light_ceiling_fan("name-1")) with patch_bond_device_state(return_value={"down_light": 0, "light": 0}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("light.name_1_down_light").state == "off"
async def test_parse_brightness(hass: core.HomeAssistant): """Tests that reported brightness level (0..100) converted to HA brightness (0...255).""" await setup_platform(hass, LIGHT_DOMAIN, dimmable_ceiling_fan("name-1")) with patch_bond_device_state(return_value={"light": 1, "brightness": 50}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("light.name_1").attributes[ATTR_BRIGHTNESS] == 128
async def test_update_reports_fan_off(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports fan power is off.""" await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1")) with patch_bond_device_state(return_value={"power": 0, "speed": 1}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("fan.name_1").state == "off"
async def test_update_reports_switch_is_off(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports the device is off.""" await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) with patch_bond_device_state(return_value={"power": 0}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("switch.name_1").state == "off"
async def test_update_reports_direction_reverse(hass: core.HomeAssistant): """Tests that update command sets correct direction when Bond API reports fan direction is reverse.""" await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1")) with patch_bond_device_state(return_value={"direction": Direction.REVERSE}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("fan.name_1").attributes[ATTR_DIRECTION] == DIRECTION_REVERSE
async def test_connection_state_changes(harmony_client, mock_hc, hass, mock_write_config): """Ensure connection changes are reflected in the switch states.""" entry = MockConfigEntry(domain=DOMAIN, data={ CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME }) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() # mocks start with current activity == Watch TV assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) harmony_client.mock_disconnection() await hass.async_block_till_done() # Entities do not immediately show as unavailable assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) future_time = utcnow() + timedelta(seconds=10) async_fire_time_changed(hass, future_time) await hass.async_block_till_done() assert hass.states.is_state(ENTITY_WATCH_TV, STATE_UNAVAILABLE) assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_UNAVAILABLE) harmony_client.mock_reconnection() await hass.async_block_till_done() assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) harmony_client.mock_disconnection() harmony_client.mock_reconnection() future_time = utcnow() + timedelta(seconds=10) async_fire_time_changed(hass, future_time) await hass.async_block_till_done() assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
async def test_update_reports_open_cover(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports cover is open.""" await setup_platform(hass, COVER_DOMAIN, shades("name-1")) with patch("homeassistant.components.bond.Bond.getDeviceState", return_value={"open": 1}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("cover.name_1").state == "open"
async def test_polling_fails_and_recovers(hass: core.HomeAssistant): """Test that polling fails and we recover.""" await setup_platform( hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id" ) with patch_bond_device_state(side_effect=asyncio.TimeoutError): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() assert hass.states.get("fan.name_1").state == STATE_UNAVAILABLE with patch_bond_device_state(return_value={"power": 1, "speed": 1}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() state = hass.states.get("fan.name_1") assert state.state == STATE_ON assert state.attributes[fan.ATTR_PERCENTAGE] == 33
async def test_update_reports_light_is_on(hass: core.HomeAssistant): """Tests that update command sets correct state when Bond API reports the light is on.""" await setup_platform(hass, LIGHT_DOMAIN, ceiling_fan("name-1")) with patch("homeassistant.components.bond.Bond.getDeviceState", return_value={"light": 1}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get("light.name_1").state == "on"
async def test_update_reports_direction_forward(hass: core.HomeAssistant): """Tests that update command sets correct direction when Bond API reports fan direction is forward.""" await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1")) with patch( "homeassistant.components.bond.Bond.getDeviceState", return_value={"direction": Directions.FORWARD}, ): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert hass.states.get( "fan.name_1").attributes[ATTR_DIRECTION] == DIRECTION_FORWARD
def trend_sensor_state_listener(entity, old_state, new_state): """Handle state changes on the observed device.""" try: if self._attribute: state = new_state.attributes.get(self._attribute) else: state = new_state.state if state != STATE_UNKNOWN: sample = (utcnow().timestamp(), float(state)) self.samples.append(sample) self.async_schedule_update_ha_state(True) except (ValueError, TypeError) as ex: _LOGGER.error(ex)
async def test_polling_stops_at_the_stop_event(hass: core.HomeAssistant): """Test that polling stops at the stop event.""" await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id") with patch_bond_device_state(side_effect=asyncio.TimeoutError): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() assert hass.states.get("fan.name_1").state == STATE_UNAVAILABLE hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) hass.state = CoreState.stopping await hass.async_block_till_done() with patch_bond_device_state(return_value={"power": 1, "speed": 1}): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() assert hass.states.get("fan.name_1").state == STATE_UNAVAILABLE
async def test_window_shuttler(hass, cube: MaxCube, windowshutter: MaxWindowShutter): """Test a successful setup with a shuttler device.""" state = hass.states.get(ENTITY_ID) assert state is not None assert state.state == STATE_ON assert state.attributes.get(ATTR_FRIENDLY_NAME) == "TestRoom TestShutter" assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_WINDOW windowshutter.is_open = False async_fire_time_changed(hass, utcnow() + timedelta(minutes=5)) await hass.async_block_till_done() state = hass.states.get(ENTITY_ID) assert state.state == STATE_OFF
async def test_flame_converted_to_brightness(hass: core.HomeAssistant): """Tests that reported flame level (0..100) converted to HA brightness (0...255).""" await setup_platform(hass, LIGHT_DOMAIN, fireplace("name-1")) with patch( "homeassistant.components.bond.Bond.getDeviceState", return_value={ "power": 1, "flame": 50 }, ): async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() _LOGGER.warning(hass.states.get("light.name_1").attributes) assert hass.states.get("light.name_1").attributes[ATTR_BRIGHTNESS] == 128
async def test_client_disconnected(hass): """Test client disconnected.""" await init_integration(hass) future = utcnow() + timedelta(minutes=60) with patch( "homeassistant.components.ruckus_unleashed.Ruckus.clients", return_value={}, ): async_fire_time_changed(hass, future) await hass.async_block_till_done() await hass.helpers.entity_component.async_update_entity( TEST_CLIENT_ENTITY_ID) test_client = hass.states.get(TEST_CLIENT_ENTITY_ID) assert test_client.state == STATE_NOT_HOME
async def test_clients_update_failed(hass): """Test failed update.""" await init_integration(hass) future = utcnow() + timedelta(minutes=60) with patch( "homeassistant.components.ruckus_unleashed.Ruckus.clients", side_effect=ConnectionError, ): async_fire_time_changed(hass, future) await hass.async_block_till_done() await hass.helpers.entity_component.async_update_entity( TEST_CLIENT_ENTITY_ID) test_client = hass.states.get(TEST_CLIENT_ENTITY_ID) assert test_client.state == STATE_UNAVAILABLE
def __init__(self, hass, username, password): from . import smartblinds #from smartblinds_client import SmartBlindsClient self._hass = hass #self._sbclient = SmartBlindsClient(username=username, password=password) self._sbclient = smartblinds.SmartBlindsClient(username=username, password=password) self._blinds = [] self._rooms = [] self._blind_states = {} self._blinds_by_mac = {} self._pending_blind_positions = {} self._cancel_pending_blind_position_timer = None self.entities = [] track_time_interval(hass, self._update_periodic, timedelta(minutes=POLLING_INTERVAL_MINUTES)) track_point_in_utc_time(hass, self._update_periodic, utcnow() + timedelta(seconds=10))
def fetch_data(connection: datapoint.Manager, site, mode) -> MetOfficeData: """Fetch weather and forecast from Datapoint API.""" try: forecast = connection.get_forecast_for_site(site.id, mode) except (ValueError, datapoint.exceptions.APIException) as err: _LOGGER.error("Check Met Office connection: %s", err.args) raise UpdateFailed from err else: time_now = utcnow() return MetOfficeData( forecast.now(), [ timestep for day in forecast.days for timestep in day.timesteps if timestep.date > time_now ], site, )
async def async_update(self): """Get the latest data and update the states.""" # Remove outdated samples if self._sample_duration > 0: cutoff = utcnow().timestamp() - self._sample_duration while self.samples and self.samples[0][0] < cutoff: self.samples.popleft() if len(self.samples) < 2: return # Calculate gradient of linear trend await self.hass.async_add_job(self._calculate_gradient) # Update state self._state = ( abs(self._gradient) > abs(self._min_gradient) and math.copysign(self._gradient, self._min_gradient) == self._gradient ) if self._invert: self._state = not self._state