async def async_step_init(self, user_input=None): """Manage the options.""" if user_input is not None: return self.async_create_entry( title=self.config_entry.data[CONF_USERNAME], data=user_input) return self.async_show_form( step_id="init", data_schema=vol.Schema({ vol.Required(CONF_TRACK_ALARMS, default=self.config_entry.options.get( CONF_TRACK_ALARMS, False)): bool, vol.Required(CONF_TRACK_DEVICES, default=self.config_entry.options.get( CONF_TRACK_DEVICES, True)): bool, #vol.Required(CONF_TRACK_NEW_DEVICES, default=self.config_entry.options.get(CONF_TRACK_NEW_DEVICES, DEFAULT_TRACK_NEW)): bool, vol.Required(CONF_CONSIDER_HOME, default=self.config_entry.options.get( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds())): int, vol.Required( CONF_RSSI_THRESHOLD, default=self.config_entry.options.get( CONF_RSSI_THRESHOLD, DEFAULT_RSSI_THRESHOLD), ): int, #vol.Required( # CONF_DEVICE_TYPES, # default=self.config_entry.data.get(CONF_DEVICE_TYPES, DEFAULT_DEVICE_TYPES), #): [vol.In(DEFAULT_DEVICE_TYPES)], }), )
async def async_get_scanner(hass: HomeAssistant, config: ConfigType) -> None: """Validate the configuration and return a Nmap scanner.""" validated_config = config[DEVICE_TRACKER_DOMAIN] if CONF_SCAN_INTERVAL in validated_config: scan_interval = validated_config[CONF_SCAN_INTERVAL].total_seconds() else: scan_interval = TRACKER_SCAN_INTERVAL if CONF_CONSIDER_HOME in validated_config: consider_home = validated_config[CONF_CONSIDER_HOME].total_seconds() else: consider_home = DEFAULT_CONSIDER_HOME.total_seconds() import_config = { CONF_HOSTS: ",".join(validated_config[CONF_HOSTS]), CONF_HOME_INTERVAL: validated_config[CONF_HOME_INTERVAL], CONF_CONSIDER_HOME: consider_home, CONF_EXCLUDE: ",".join(validated_config[CONF_EXCLUDE]), CONF_OPTIONS: validated_config[CONF_OPTIONS], CONF_SCAN_INTERVAL: scan_interval, } hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, data=import_config, ) ) _LOGGER.warning( "Your Nmap Tracker configuration has been imported into the UI, " "please remove it from configuration.yaml. " )
async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle options flow.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) data_schema = vol.Schema( { vol.Optional( CONF_CONSIDER_HOME, default=self.config_entry.options.get( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds() ), ): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=900)), vol.Optional( CONF_OLD_DISCOVERY, default=self.config_entry.options.get( CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY ), ): bool, } ) return self.async_show_form(step_id="init", data_schema=data_schema)
async def test_user(hass: HomeAssistant, fc_class_mock): """Test starting a flow by user.""" with patch( "homeassistant.components.fritz.common.FritzConnection", side_effect=fc_class_mock, ), patch("homeassistant.components.fritz.common.FritzStatus"), patch( "homeassistant.components.fritz.async_setup_entry" ) as mock_setup_entry: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}) assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == "user" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=MOCK_USER_DATA) assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["data"][CONF_HOST] == "fake_host" assert result["data"][CONF_PASSWORD] == "fake_pass" assert result["data"][CONF_USERNAME] == "fake_user" assert (result["options"][CONF_CONSIDER_HOME] == DEFAULT_CONSIDER_HOME.total_seconds()) assert not result["result"].unique_id await hass.async_block_till_done() assert mock_setup_entry.called
def scan_devices(self, now: datetime | None = None) -> None: """Scan for new devices and return a list of found device ids.""" _LOGGER.debug("Checking devices for FRITZ!Box router %s", self.host) if self._options: consider_home = self._options.get( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds()) else: consider_home = DEFAULT_CONSIDER_HOME new_device = False for known_host in self._update_info(): if not known_host.get("mac"): continue dev_mac = known_host["mac"] dev_name = known_host["name"] dev_ip = known_host["ip"] dev_home = known_host["status"] dev_info = Device(dev_mac, dev_ip, dev_name) if dev_mac in self._devices: self._devices[dev_mac].update(dev_info, dev_home, consider_home) else: device = FritzDevice(dev_mac, dev_name) device.update(dev_info, dev_home, consider_home) self._devices[dev_mac] = device new_device = True dispatcher_send(self.hass, self.signal_device_update) if new_device: dispatcher_send(self.hass, self.signal_device_new)
async def _async_build_schema_with_user_input( hass: HomeAssistant, user_input: dict[str, Any], include_options: bool) -> vol.Schema: hosts = user_input.get(CONF_HOSTS, await async_get_network(hass)) exclude = user_input.get( CONF_EXCLUDE, await network.async_get_source_ip(hass, MDNS_TARGET_IP)) schema = { vol.Required(CONF_HOSTS, default=hosts): str, vol.Required(CONF_HOME_INTERVAL, default=user_input.get(CONF_HOME_INTERVAL, 0)): int, vol.Optional(CONF_EXCLUDE, default=exclude): str, vol.Optional(CONF_OPTIONS, default=user_input.get(CONF_OPTIONS, DEFAULT_OPTIONS)): str, } if include_options: schema.update({ vol.Optional( CONF_SCAN_INTERVAL, default=user_input.get(CONF_SCAN_INTERVAL, TRACKER_SCAN_INTERVAL), ): vol.All(vol.Coerce(int), vol.Range(min=10, max=MAX_SCAN_INTERVAL)), vol.Optional( CONF_CONSIDER_HOME, default=user_input.get(CONF_CONSIDER_HOME) or DEFAULT_CONSIDER_HOME.total_seconds( ), ): vol.All(vol.Coerce(int), vol.Range(min=1, max=MAX_CONSIDER_HOME)), }) return vol.Schema(schema)
async def test_options_reload(hass: HomeAssistant, fc_class_mock, fh_class_mock): """Test reload of Fritz!Tools, when options changed.""" entry = MockConfigEntry( domain=DOMAIN, data=MOCK_USER_DATA, options={CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds()}, ) entry.add_to_hass(hass) with patch( "homeassistant.config_entries.ConfigEntries.async_reload", return_value=None, ) as mock_reload: assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() assert entry.state == ConfigEntryState.LOADED result = await hass.config_entries.options.async_init(entry.entry_id) await hass.async_block_till_done() await hass.config_entries.options.async_configure( result["flow_id"], user_input={CONF_CONSIDER_HOME: 60}, ) await hass.async_block_till_done() mock_reload.assert_called_once()
def _async_create_entry(self) -> FlowResult: """Async create flow handler entry.""" return self.async_create_entry( title=self._name, data={ CONF_HOST: self.fritz_tools.host, CONF_PASSWORD: self.fritz_tools.password, CONF_PORT: self.fritz_tools.port, CONF_USERNAME: self.fritz_tools.username, }, options={ CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds(), }, )
def scan_devices(self, now: datetime | None = None) -> None: """Scan for new devices and return a list of found device ids.""" _LOGGER.debug("Checking devices for FRITZ!Box router %s", self.host) _default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds() if self._options: consider_home = self._options.get(CONF_CONSIDER_HOME, _default_consider_home) else: consider_home = _default_consider_home new_device = False for known_host in self._update_hosts_info(): if not known_host.get("mac"): continue dev_mac = known_host["mac"] dev_name = known_host["name"] dev_ip = known_host["ip"] dev_home = known_host["status"] dev_wan_access = True if dev_ip: wan_access = self.connection.call_action( "X_AVM-DE_HostFilter:1", "GetWANAccessByIP", NewIPv4Address=dev_ip, ) if wan_access: dev_wan_access = not wan_access.get("NewDisallow") dev_info = Device(dev_mac, dev_ip, dev_name, dev_wan_access) if dev_mac in self._devices: self._devices[dev_mac].update(dev_info, dev_home, consider_home) else: device = FritzDevice(dev_mac, dev_name) device.update(dev_info, dev_home, consider_home) self._devices[dev_mac] = device new_device = True dispatcher_send(self.hass, self.signal_device_update) if new_device: dispatcher_send(self.hass, self.signal_device_new) _LOGGER.debug("Checking host info for FRITZ!Box router %s", self.host) self._update_available, self._latest_firmware = self._update_device_info( )
async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle options flow.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) data_schema = vol.Schema( { vol.Optional( CONF_CONSIDER_HOME, default=self.config_entry.options.get( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds() ), ): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=900)), vol.Optional( CONF_TRACK_UNKNOWN, default=self.config_entry.options.get( CONF_TRACK_UNKNOWN, DEFAULT_TRACK_UNKNOWN ), ): bool, vol.Required( CONF_INTERFACE, default=self.config_entry.options.get( CONF_INTERFACE, DEFAULT_INTERFACE ), ): str, vol.Required( CONF_DNSMASQ, default=self.config_entry.options.get( CONF_DNSMASQ, DEFAULT_DNSMASQ ), ): str, } ) if self.config_entry.data[CONF_MODE] == MODE_AP: data_schema = data_schema.extend( { vol.Optional( CONF_REQUIRE_IP, default=self.config_entry.options.get(CONF_REQUIRE_IP, True), ): bool, } ) return self.async_show_form(step_id="init", data_schema=data_schema)
async def update_devices(self) -> None: """Update AsusWrt devices tracker.""" new_device = False _LOGGER.debug("Checking devices for ASUS router %s", self._host) try: api_devices = await self._api.async_get_connected_devices() except OSError as exc: if not self._connect_error: self._connect_error = True _LOGGER.error( "Error connecting to ASUS router %s for device update: %s", self._host, exc, ) return if self._connect_error: self._connect_error = False _LOGGER.info("Reconnected to ASUS router %s", self._host) self._connected_devices = len(api_devices) consider_home = self._options.get( CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds()) track_unknown = self._options.get(CONF_TRACK_UNKNOWN, DEFAULT_TRACK_UNKNOWN) wrt_devices = { format_mac(mac): dev for mac, dev in api_devices.items() } for device_mac, device in self._devices.items(): dev_info = wrt_devices.pop(device_mac, None) device.update(dev_info, consider_home) for device_mac, dev_info in wrt_devices.items(): if not track_unknown and not dev_info.name: continue new_device = True device = AsusWrtDevInfo(device_mac) device.update(dev_info) self._devices[device_mac] = device async_dispatcher_send(self.hass, self.signal_device_update) if new_device: async_dispatcher_send(self.hass, self.signal_device_new) await self._update_unpolled_sensors()
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Migrate config entry.""" _LOGGER.debug("Migrating from version %s", config_entry.version) if config_entry.version == 1: config_entry.version = 2 hass.config_entries.async_update_entry( config_entry, options={ CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds() }, ) _LOGGER.info("Migration to version %s successful", config_entry.version) return True
def __init__(self, hass, client, config, device): """Initialize the scanner.""" super().__init__( hass, timedelta(seconds=config.options.get(CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds())), #config.options.get(CONF_TRACK_NEW_DEVICES, DEFAULT_TRACK_NEW), True, # TODO migrate to the new device tracker api {}, list(), ) self.hass = hass self.rssi = config.options.get(CONF_RSSI_THRESHOLD, DEFAULT_RSSI_THRESHOLD) self.device_types = config.options.get(CONF_DEVICE_TYPES, DEFAULT_DEVICE_TYPES) self.host = device.host self.uuid = device.uuid self.name = device.friendly_name self.client = client self.config_entry = config self.removal = None self.active = True
async def test_user(hass: HomeAssistant, fc_class_mock, mock_get_source_ip): """Test starting a flow by user.""" with patch( "homeassistant.components.fritz.common.FritzConnection", side_effect=fc_class_mock, ), patch("homeassistant.components.fritz.common.FritzStatus"), patch( "homeassistant.components.fritz.common.FritzBoxTools._update_device_info", return_value=MOCK_FIRMWARE_INFO, ), patch( "homeassistant.components.fritz.async_setup_entry" ) as mock_setup_entry, patch( "requests.get" ) as mock_request_get, patch( "requests.post" ) as mock_request_post: mock_request_get.return_value.status_code = 200 mock_request_get.return_value.content = MOCK_REQUEST mock_request_post.return_value.status_code = 200 mock_request_post.return_value.text = MOCK_REQUEST result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == "user" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=MOCK_USER_DATA ) assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["data"][CONF_HOST] == "fake_host" assert result["data"][CONF_PASSWORD] == "fake_pass" assert result["data"][CONF_USERNAME] == "fake_user" assert ( result["options"][CONF_CONSIDER_HOME] == DEFAULT_CONSIDER_HOME.total_seconds() ) assert not result["result"].unique_id await hass.async_block_till_done() assert mock_setup_entry.called
"""Constants used in the Keenetic NDMS2 components.""" from homeassistant.components.device_tracker.const import ( DEFAULT_CONSIDER_HOME as _DEFAULT_CONSIDER_HOME, ) DOMAIN = "keenetic_ndms2" ROUTER = "router" UNDO_UPDATE_LISTENER = "undo_update_listener" DEFAULT_TELNET_PORT = 23 DEFAULT_SCAN_INTERVAL = 120 DEFAULT_CONSIDER_HOME = _DEFAULT_CONSIDER_HOME.total_seconds() DEFAULT_INTERFACE = "Home" CONF_CONSIDER_HOME = "consider_home" CONF_INTERFACES = "interfaces" CONF_TRY_HOTSPOT = "try_hotspot" CONF_INCLUDE_ARP = "include_arp" CONF_INCLUDE_ASSOCIATED = "include_associated" CONF_LEGACY_INTERFACE = "interface"
CONF_HOME_INTERVAL, CONF_OPTIONS, DEFAULT_OPTIONS, DOMAIN, TRACKER_SCAN_INTERVAL, ) _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = DEVICE_TRACKER_PLATFORM_SCHEMA.extend( { vol.Required(CONF_HOSTS): cv.ensure_list, vol.Required(CONF_HOME_INTERVAL, default=0): cv.positive_int, vol.Required( CONF_CONSIDER_HOME, default=DEFAULT_CONSIDER_HOME.total_seconds() ): cv.time_period, vol.Optional(CONF_EXCLUDE, default=[]): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_OPTIONS, default=DEFAULT_OPTIONS): cv.string, } ) async def async_get_scanner(hass: HomeAssistant, config: ConfigType) -> None: """Validate the configuration and return a Nmap scanner.""" validated_config = config[DEVICE_TRACKER_DOMAIN] if CONF_SCAN_INTERVAL in validated_config: scan_interval = validated_config[CONF_SCAN_INTERVAL].total_seconds() else: scan_interval = TRACKER_SCAN_INTERVAL
def scan_devices(self, now: datetime | None = None) -> None: """Scan for new devices and return a list of found device ids.""" _LOGGER.debug("Checking host info for FRITZ!Box router %s", self.host) self._update_available, self._latest_firmware = self._update_device_info( ) try: topology = self.fritz_hosts.get_mesh_topology() except FritzActionError: self.mesh_role = MeshRoles.SLAVE return _LOGGER.debug("Checking devices for FRITZ!Box router %s", self.host) _default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds() if self._options: consider_home = self._options.get(CONF_CONSIDER_HOME, _default_consider_home) else: consider_home = _default_consider_home new_device = False hosts = {} for host in self._update_hosts_info(): if not host.get("mac"): continue hosts[host["mac"]] = Device( name=host["name"], connected=host["status"], connected_to="", connection_type="", ip_address=host["ip"], ssid=None, wan_access=False, ) mesh_intf = {} # first get all meshed devices for node in topology["nodes"]: if not node["is_meshed"]: continue for interf in node["node_interfaces"]: int_mac = interf["mac_address"] mesh_intf[interf["uid"]] = Interface( device=node["device_name"], mac=int_mac, op_mode=interf.get("op_mode", ""), ssid=interf.get("ssid", ""), type=interf["type"], ) if format_mac(int_mac) == format_mac(self.mac): self.mesh_role = MeshRoles(node["mesh_role"]) # second get all client devices for node in topology["nodes"]: if node["is_meshed"]: continue for interf in node["node_interfaces"]: dev_mac = interf["mac_address"] for link in interf["node_links"]: intf = mesh_intf.get(link["node_interface_1_uid"]) if (intf is not None and link["state"] == "CONNECTED" and dev_mac in hosts): dev_info: Device = hosts[dev_mac] if intf["op_mode"] != "AP_GUEST": dev_info.wan_access = not self.connection.call_action( "X_AVM-DE_HostFilter:1", "GetWANAccessByIP", NewIPv4Address=dev_info.ip_address, ).get("NewDisallow") dev_info.connected_to = intf["device"] dev_info.connection_type = intf["type"] dev_info.ssid = intf.get("ssid") if dev_mac in self._devices: self._devices[dev_mac].update( dev_info, consider_home) else: device = FritzDevice(dev_mac, dev_info.name) device.update(dev_info, consider_home) self._devices[dev_mac] = device new_device = True dispatcher_send(self.hass, self.signal_device_update) if new_device: dispatcher_send(self.hass, self.signal_device_new)