Ejemplo n.º 1
0
def on_message(client, userdata, msg):
    auth = {
        'username': dataMap["mqtt"]["user"],
        'password': dataMap["mqtt"]["password"]
    }
    topic = str(msg.topic)
    payload = (msg.payload).decode("utf-8")
    state = int(payload)
    print((topic) + " : " + (payload))
    for info in dataMap["fritzBoxMain"]["switches"]:
        if info["setTopic"] == topic:
            command = info["command"]
            print(command + ": " + str(state))
            fc = FritzConnection(dataMap["fritzBoxMain"]["ip"],
                                 password=dataMap["fritzBoxMain"]["password"])
            fc.call_action(command, 'SetEnable', NewEnable=state)
            state = str(int(fc.call_action(command, 'GetInfo')["NewEnable"]))
            print("State: " + state)
            try:
                publish.single(info["getTopic"],
                               payload=state,
                               hostname=dataMap["mqtt"]["serverIP"],
                               auth=auth)
            except Exception as e:
                print('Failed: ' + str(e))
            print("published")
Ejemplo n.º 2
0
def main():
  try:
    with open(os.environ['CONFIG_PATH'], 'r') as f:
      conf = yaml.safe_load(f.read())
  except:
    print("Error getting config file")
    exit()

  fc = FritzConnection(conf['fritzbox']['address'])
  if conf['ipv6']['enabled']:
    if conf['ipv6']['target'] == '':
      print('IPv6 is enabled but not target host has been set.')
      exit()

    ipv6_prefix = ':'.join(fc.call_action('WANIPConn1', 'X_AVM_DE_GetIPv6Prefix')['NewIPv6Prefix'].split(':')[0:4])

    external_ipv6 = f"{ ipv6_prefix }:{ conf['ipv6']['target'] }"
  else:
    external_ipv6 = ''

  # print(external_ipv6)
  external_ipv4 = fc.call_action('WANIPConn1', 'GetExternalIPAddress')['NewExternalIPAddress']

  if 'namecheap' in conf:
    c = ncc.NamecheapClient(conf['namecheap'])
  elif 'cloudflare' in conf:
    c = cfc.CloudFlareClient(conf['cloudflare'])
  else:
    exit()

  existing = c.get_dns_records(conf['domain'])

  c.set_dns_records(conf['records'], existing, external_ipv4, external_ipv6)
Ejemplo n.º 3
0
def print_values():
    try:
        conn = FritzConnection(address=os.environ['fritzbox_ip'])
    except Exception as e:
        sys.exit("Couldn't get WAN traffic")

    down_traffic = conn.call_action(
        'WANCommonInterfaceConfig',
        'GetTotalBytesReceived')['NewTotalBytesReceived']
    print('down.value %d' % down_traffic)

    up_traffic = conn.call_action('WANCommonInterfaceConfig',
                                  'GetTotalBytesSent')['NewTotalBytesSent']
    print('up.value %d' % up_traffic)

    if not os.environ.get('traffic_remove_max'):
        max_down_traffic = conn.call_action(
            'WANCommonInterfaceConfig',
            'GetCommonLinkProperties')['NewLayer1DownstreamMaxBitRate']
        print('maxdown.value %d' % max_down_traffic)

        max_up_traffic = conn.call_action(
            'WANCommonInterfaceConfig',
            'GetCommonLinkProperties')['NewLayer1UpstreamMaxBitRate']
        print('maxup.value %d' % max_up_traffic)
Ejemplo n.º 4
0
    def fritz_tools_init(self) -> str | None:
        """Initialize FRITZ!Box Tools class."""

        try:
            connection = FritzConnection(
                address=self._host,
                port=self._port,
                user=self._username,
                password=self._password,
                timeout=60.0,
                pool_maxsize=30,
            )
        except FritzSecurityError:
            return ERROR_AUTH_INVALID
        except FritzConnectionException:
            return ERROR_CANNOT_CONNECT
        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception("Unexpected exception")
            return ERROR_UNKNOWN

        self._model = connection.call_action("DeviceInfo:1",
                                             "GetInfo")["NewModelName"]

        if ("X_AVM-DE_UPnP1" in connection.services
                and not connection.call_action("X_AVM-DE_UPnP1",
                                               "GetInfo")["NewEnable"]):
            return ERROR_UPNP_NOT_CONFIGURED

        return None
Ejemplo n.º 5
0
    def _try_connect(self):
        """Try to connect and check auth."""
        self._fritzbox_phonebook = FritzBoxPhonebook(
            host=self._host,
            username=self._username,
            password=self._password,
            phonebook_id=self._phonebook_id,
            prefixes=self._prefixes,
        )

        try:
            self._fritzbox_phonebook.init_phonebook()
            self._phonebook_ids = self._fritzbox_phonebook.get_phonebook_ids()

            fritz_connection = FritzConnection(address=self._host,
                                               user=self._username,
                                               password=self._password)
            device_info = fritz_connection.call_action(
                FRITZ_SERVICE_DEVICE_INFO, FRITZ_ACTION_GET_INFO)
            self._serial_number = device_info[FRITZ_ATTR_SERIAL_NUMBER]

            return RESULT_SUCCESS
        except RequestsConnectionError:
            return RESULT_NO_DEVIES_FOUND
        except FritzSecurityError:
            return RESULT_INSUFFICIENT_PERMISSIONS
        except FritzConnectionException:
            return RESULT_INVALID_AUTH
Ejemplo n.º 6
0
class FritzDevice():
    def __init__(self, host, user, password) -> None:
        self.host = host
        self.serial = "n/a"
        self.model = "n/a"

        try:
            self.fc = FritzConnection(address=host,
                                      user=user,
                                      password=password)
        except ConnectionError as e:
            logger.critical(f'unable to connect to {host}: {str(e)}',
                            exc_info=True)
            sys.exit(1)

        self.capabilities = FritzCapabilities(self)

        self.getDeviceInfo()

        if self.capabilities.empty():
            logger.critical(
                f'Device {host} has no detected capabilities. Exiting. ')
            sys.exit(1)

    def getDeviceInfo(self):
        try:
            device_info = self.fc.call_action('DeviceInfo', 'GetInfo')
            self.serial = device_info['NewSerialNumber']
            self.model = device_info['NewModelName']

        except (FritzServiceError, FritzActionError):
            logger.error(
                f'Fritz Device {self.host} does not provide basic device info (Service: DeviceInfo1, Action: GetInfo). serial number and model name will be unavailable.',
                exc_info=True)
def print_values():
    server = os.environ['fritzbox_ip']
    if 'fritzbox_port' in os.environ:
        port = os.environ['fritzbox_port']
    else:
        port = None
    if 'fritzbox_user' in os.environ:
        user = os.environ['fritzbox_user']
    else:
        user = None
    if 'fritzbox_password' in os.environ:
        password = os.environ['fritzbox_password']
    else:
        password = None

    try:
        conn = FritzConnection(address=server,
                               port=port,
                               user=user,
                               password=password)
    except Exception as e:
        sys.exit("Couldn't get connection uptime", e)

    uptime = conn.call_action('WANIPConn', 'GetStatusInfo')['NewUptime']
    print('uptime.value %.2f' % (int(uptime) / 86400.0))
Ejemplo n.º 8
0
def print_uptime():
    try:
        conn = FritzConnection(address=os.environ['fritzbox_ip'])
    except Exception as e:
        sys.exit("Couldn't get connection uptime")

    uptime = conn.call_action('WANIPConnection', 'GetStatusInfo')['NewUptime']
    print('uptime.value %.2f' % (int(uptime) / 86400.0))
def print_values():
    try:
        conn = FritzConnection(address=os.environ['fritzbox_ip'])
    except Exception as e:
        sys.exit("Couldn't get connection uptime")

    uptime = conn.call_action('WANIPConnection', 'GetStatusInfo')['NewUptime']
    print('uptime.value %.2f' % (int(uptime) / 86400.0))
def print_values():
    try:
        conn = FritzConnection()
    except Exception as e:
        sys.exit("Couldn't get connection uptime")

    uptime = conn.call_action('WANIPConnection', 'GetStatusInfo')['NewUptime']
    print('uptime.value %.2f' % (int(uptime) / 86400.0))
Ejemplo n.º 11
0
def print_values():
    try:
        conn = FritzConnection()
    except Exception as e:
        sys.exit("Couldn't get WAN traffic")

    down_traffic = conn.call_action('WANCommonInterfaceConfig', 'GetTotalBytesReceived')['NewTotalBytesReceived']
    print ('down.value %d' % down_traffic)

    up_traffic = conn.call_action('WANCommonInterfaceConfig', 'GetTotalBytesSent')['NewTotalBytesSent']
    print ('up.value %d' % up_traffic)

    max_down_traffic = conn.call_action('WANCommonInterfaceConfig', 'GetCommonLinkProperties')[
        'NewLayer1DownstreamMaxBitRate']
    print ('maxdown.value %d' % max_down_traffic)

    max_up_traffic = conn.call_action('WANCommonInterfaceConfig', 'GetCommonLinkProperties')[
        'NewLayer1UpstreamMaxBitRate']
    print ('maxup.value %d' % max_up_traffic)
Ejemplo n.º 12
0
def print_values():
    server = os.environ['fritzbox_ip']
    if 'fritzbox_port' in os.environ:
        port = os.environ['fritzbox_port']
    else:
        port = None
    if 'fritzbox_user' in os.environ:
        user = os.environ['fritzbox_user']
    else:
        user = None
    if 'fritzbox_password' in os.environ:
        password = os.environ['fritzbox_password']
    else:
        password = None

    try:
        conn = FritzConnection(address=os.environ['fritzbox_ip'],
                               port=port,
                               user=user,
                               password=password)
    except Exception as e:
        sys.exit("Couldn't get WAN traffic")

    down_traffic = conn.call_action(
        'WANCommonIFC', 'GetTotalBytesReceived')['NewTotalBytesReceived']
    print('down.value %d' % down_traffic)

    up_traffic = conn.call_action('WANCommonIFC',
                                  'GetTotalBytesSent')['NewTotalBytesSent']
    print('up.value %d' % up_traffic)

    if not os.environ.get('traffic_remove_max'):
        max_down_traffic = conn.call_action(
            'WANCommonIFC',
            'GetCommonLinkProperties')['NewLayer1DownstreamMaxBitRate']
        print('maxdown.value %d' % max_down_traffic)

        max_up_traffic = conn.call_action(
            'WANCommonIFC',
            'GetCommonLinkProperties')['NewLayer1UpstreamMaxBitRate']
        print('maxup.value %d' % max_up_traffic)
Ejemplo n.º 13
0
    def _probe(self) -> Optional[ValueSet]:
        connection = FritzConnection(address=self._address, password=self._pass, timeout=10)
        new_data = {}
        service_name = 'WANCommonInterfaceConfig:1'
        if service_name not in connection.services:
            # Use legacy fallback
            service_name = 'WANCommonIFC1'
        output = connection.call_action(service_name, 'GetTotalBytesReceived')
        new_data['recv_bytes_sec'] = output['NewTotalBytesReceived']

        output = connection.call_action(service_name, 'GetTotalBytesSent')
        new_data['sent_bytes_sec'] = output['NewTotalBytesSent']

        data = ValueSet()
        for key, value in new_data.items():
            last_stats = self._stats.get(key)
            self._stats[key] = value
            if last_stats is not None:
                time_delta = int(time.time() - self._last_time)
                data.add(Value(max(0, (value - last_stats) / time_delta), name=key))

        self._last_time = time.time()
        return data
Ejemplo n.º 14
0
class FritzDevice(object):

    fc = None
    ip = None
    password = None

    def __init__(self, ip, password):
        from fritzconnection import FritzConnection
        self.fc = FritzConnection(address=ip, password=password)
        self.ip = ip
        self.password = password

    def get_data(self, service, action):
        return self.fc.call_action(service, action)

    @property
    def services(self):
        import fritzconnection as _fc
        return _fc.print_api(address=self.ip, password=self.password)
def retrieveSmartHomeTemps():
    smartHomeData = []
    config = FritzboxConfig()

    try:
      connection = FritzConnection(address=config.server, password=config.password, use_tls=config.useTls)
    except Exception as e:
      sys.exit("Couldn't get temperature: " + str(e))

    for i in range(0, 20):
      try:
        data = connection.call_action('X_AVM-DE_Homeauto1', 'GetGenericDeviceInfos', arguments={'NewIndex': i})
        if (data['NewTemperatureIsEnabled']):
          smartHomeData.append(data)
      except Exception as e:
        # smart home device index does not exist, so we stop here
        break

    return smartHomeData
Ejemplo n.º 16
0
def get_values():
    try:
        conn = FritzConnection(address='192.168.178.1')
    except Exception as e:
        print("cannot connect to fritzbox")

    info = conn.call_action('WANCommonIFC', 'GetAddonInfos')

    collection = []

    currentDownstream = info["NewByteReceiveRate"]
    collection.append(get_json("WANTraffic", currentDownstream, "fritzbox", "down", "current"))
    currentUpstream = info["NewByteSendRate"]
    collection.append(get_json("WANTraffic", currentUpstream, "fritzbox", "up", "current"))
    totalDownstream = int(info["NewX_AVM_DE_TotalBytesReceived64"])
    collection.append(get_json("WANTraffic", totalDownstream, "fritzbox", "down", "total"))
    totalUpstream =  int(info["NewX_AVM_DE_TotalBytesSent64"])
    collection.append(get_json("WANTraffic", totalUpstream, "fritzbox", "up", "total"))

    return collection
Ejemplo n.º 17
0
def get_list_from_device(conn: fritzconnection.FritzConnection) -> list:
    """
    Fetches the logfile from the connected device
    and turns it into a list of LogEntry objects.

    Args:
        conn (fritzconnection.FritzConnection): Connection object to connect to.

    Returns:
        log_entries (list): List of LogEntry objects created
        from the devices' logfile, sorted by timestamp.
    """

    log_dict = conn.call_action("DeviceInfo1", "GetDeviceLog")
    log_lines = log_dict.get("NewDeviceLog", "").split("\n")

    log_entries = []

    for line in log_lines:
        log_entries.append(classes.LogEntry(logstring=line, msg_offset=18))

    log_entries.sort(key=lambda entry: entry.timestamp)

    return log_entries
Ejemplo n.º 18
0
class FritzBoxTools(update_coordinator.DataUpdateCoordinator):
    """FrtizBoxTools class."""
    def __init__(
        self,
        hass: HomeAssistant,
        password: str,
        username: str = DEFAULT_USERNAME,
        host: str = DEFAULT_HOST,
        port: int = DEFAULT_PORT,
    ) -> None:
        """Initialize FritzboxTools class."""
        super().__init__(
            hass=hass,
            logger=_LOGGER,
            name=f"{DOMAIN}-{host}-coordinator",
            update_interval=timedelta(seconds=30),
        )

        self._devices: dict[str, FritzDevice] = {}
        self._options: MappingProxyType[str, Any] | None = None
        self._unique_id: str | None = None
        self.connection: FritzConnection = None
        self.fritz_hosts: FritzHosts = None
        self.fritz_status: FritzStatus = None
        self.hass = hass
        self.host = host
        self.password = password
        self.port = port
        self.username = username
        self._mac: str | None = None
        self._model: str | None = None
        self._current_firmware: str | None = None
        self._latest_firmware: str | None = None
        self._update_available: bool = False

    async def async_setup(self,
                          options: MappingProxyType[str, Any] | None = None
                          ) -> None:
        """Wrap up FritzboxTools class setup."""
        self._options = options
        await self.hass.async_add_executor_job(self.setup)

    def setup(self) -> None:
        """Set up FritzboxTools class."""
        self.connection = FritzConnection(
            address=self.host,
            port=self.port,
            user=self.username,
            password=self.password,
            timeout=60.0,
            pool_maxsize=30,
        )

        if not self.connection:
            _LOGGER.error("Unable to establish a connection with %s",
                          self.host)
            return

        self.fritz_status = FritzStatus(fc=self.connection)
        info = self.connection.call_action("DeviceInfo:1", "GetInfo")
        if not self._unique_id:
            self._unique_id = info["NewSerialNumber"]

        self._model = info.get("NewModelName")
        self._current_firmware = info.get("NewSoftwareVersion")

        self._update_available, self._latest_firmware = self._update_device_info(
        )

    @callback
    async def _async_update_data(self) -> None:
        """Update FritzboxTools data."""
        try:
            self.fritz_hosts = FritzHosts(fc=self.connection)
            await self.async_scan_devices()
        except (FritzSecurityError, FritzConnectionException) as ex:
            raise update_coordinator.UpdateFailed from ex

    @property
    def unique_id(self) -> str:
        """Return unique id."""
        if not self._unique_id:
            raise ClassSetupMissing()
        return self._unique_id

    @property
    def model(self) -> str:
        """Return device model."""
        if not self._model:
            raise ClassSetupMissing()
        return self._model

    @property
    def current_firmware(self) -> str:
        """Return current SW version."""
        if not self._current_firmware:
            raise ClassSetupMissing()
        return self._current_firmware

    @property
    def latest_firmware(self) -> str | None:
        """Return latest SW version."""
        return self._latest_firmware

    @property
    def update_available(self) -> bool:
        """Return if new SW version is available."""
        return self._update_available

    @property
    def mac(self) -> str:
        """Return device Mac address."""
        if not self._unique_id:
            raise ClassSetupMissing()
        return self._unique_id

    @property
    def devices(self) -> dict[str, FritzDevice]:
        """Return devices."""
        return self._devices

    @property
    def signal_device_new(self) -> str:
        """Event specific per FRITZ!Box entry to signal new device."""
        return f"{DOMAIN}-device-new-{self._unique_id}"

    @property
    def signal_device_update(self) -> str:
        """Event specific per FRITZ!Box entry to signal updates in devices."""
        return f"{DOMAIN}-device-update-{self._unique_id}"

    def _update_hosts_info(self) -> list[HostInfo]:
        """Retrieve latest hosts information from the FRITZ!Box."""
        try:
            return self.fritz_hosts.get_hosts_info(
            )  # type: ignore [no-any-return]
        except Exception as ex:  # pylint: disable=[broad-except]
            if not self.hass.is_stopping:
                raise HomeAssistantError("Error refreshing hosts info") from ex
        return []

    def _update_device_info(self) -> tuple[bool, str | None]:
        """Retrieve latest device information from the FRITZ!Box."""
        version = self.connection.call_action(
            "UserInterface1", "GetInfo").get("NewX_AVM-DE_Version")
        return bool(version), version

    async def async_scan_devices(self, now: datetime | None = None) -> None:
        """Wrap up FritzboxTools class scan."""
        await self.hass.async_add_executor_job(self.scan_devices, now)

    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_trigger_firmware_update(self) -> bool:
        """Trigger firmware update."""
        results = await self.hass.async_add_executor_job(
            self.connection.call_action, "UserInterface:1",
            "X_AVM-DE_DoUpdate")
        return cast(bool, results["NewX_AVM-DE_UpdateState"])

    async def async_trigger_reboot(self) -> None:
        """Trigger device reboot."""
        await self.hass.async_add_executor_job(self.connection.call_action,
                                               "DeviceConfig1", "Reboot")

    async def async_trigger_reconnect(self) -> None:
        """Trigger device reconnect."""
        await self.hass.async_add_executor_job(self.connection.call_action,
                                               "WANIPConn1",
                                               "ForceTermination")

    async def service_fritzbox(self, service_call: ServiceCall,
                               config_entry: ConfigEntry) -> None:
        """Define FRITZ!Box services."""
        _LOGGER.debug("FRITZ!Box router: %s", service_call.service)

        if not self.connection:
            raise HomeAssistantError("Unable to establish a connection")

        try:
            if service_call.service == SERVICE_REBOOT:
                _LOGGER.warning(
                    'Service "fritz.reboot" is deprecated, please use the corresponding button entity instead'
                )
                await self.hass.async_add_executor_job(
                    self.connection.call_action, "DeviceConfig1", "Reboot")
                return

            if service_call.service == SERVICE_RECONNECT:
                _LOGGER.warning(
                    'Service "fritz.reconnect" is deprecated, please use the corresponding button entity instead'
                )
                await self.hass.async_add_executor_job(
                    self.connection.call_action,
                    "WANIPConn1",
                    "ForceTermination",
                )
                return

            if service_call.service == SERVICE_CLEANUP:
                device_hosts_list: list = await self.hass.async_add_executor_job(
                    self.fritz_hosts.get_hosts_info)

        except (FritzServiceError, FritzActionError) as ex:
            raise HomeAssistantError("Service or parameter unknown") from ex
        except FritzConnectionException as ex:
            raise HomeAssistantError("Service not supported") from ex

        entity_reg: EntityRegistry = (
            await self.hass.helpers.entity_registry.async_get_registry())

        ha_entity_reg_list: list[
            RegistryEntry] = self.hass.helpers.entity_registry.async_entries_for_config_entry(
                entity_reg, config_entry.entry_id)
        entities_removed: bool = False

        device_hosts_macs = {device["mac"] for device in device_hosts_list}

        for entry in ha_entity_reg_list:
            if (not _cleanup_entity_filter(entry)
                    or entry.unique_id.split("_")[0] in device_hosts_macs):
                continue
            _LOGGER.info("Removing entity: %s", entry.name
                         or entry.original_name)
            entity_reg.async_remove(entry.entity_id)
            entities_removed = True

        if entities_removed:
            self._async_remove_empty_devices(entity_reg, config_entry)

    @callback
    def _async_remove_empty_devices(self, entity_reg: EntityRegistry,
                                    config_entry: ConfigEntry) -> None:
        """Remove devices with no entities."""

        device_reg = async_get(self.hass)
        device_list = async_entries_for_config_entry(device_reg,
                                                     config_entry.entry_id)
        for device_entry in device_list:
            if not async_entries_for_device(
                    entity_reg,
                    device_entry.id,
                    include_disabled_entities=True,
            ):
                _LOGGER.info("Removing device: %s", device_entry.name)
                device_reg.async_remove_device(device_entry.id)
Ejemplo n.º 19
0
    args = parser.parse_args()

    # API connection
    fc = FritzConnection(address=fritz_address, user=fritz_user, password=fritz_password)

    if args.service == "WirelessGuestAccess":
        # Servicenummer des Gäste-WLAN finden
        print("Searching for wireless guest access with SSID", guest_ssid)
        fwlan = FritzWLAN(address=fritz_address, user=fritz_user, password=fritz_password)
        service = find_guest_wlan(fwlan, guest_ssid)
        if service is None:
            print("FATAL: no wireless guest access found")
            exit(1)
        fwlan.service = service
        # aktuellen Zustand lesen
        old_enable = fc.call_action(service_name="WLANConfiguration" + str(fwlan.service),
                                    action_name="GetInfo")["NewEnable"]
        # neuen Zustand bestimmen
        if args.switch == "on":
            new_enable = True
        elif args.switch == "off":
            new_enable = False
        else:
            new_enable = not old_enable
        if old_enable != new_enable:
            tofo = {True: "on", False: "off"}
            print("Switching wireless guest access from", tofo[old_enable], "to", tofo[new_enable])
            fc.call_action(service_name="WLANConfiguration" + str(fwlan.service),
                           action_name="SetEnable", NewEnable=new_enable)
        else:
            print("No change in wireless guest access required")
    elif args.service == "SmartHome":
Ejemplo n.º 20
0
                    help="Phone number to call")
args = parser.parse_args()

item_address = "http://" + args.openhab_ip + ":8080/rest/items/" + args.item
signal.signal(signal.SIGINT, exithandler)
rfdevice = RFDevice(args.gpio)
rfdevice.enable_rx()

if args.fritzbox_ip:
    fritz_connection = FritzConnection(address=args.fritzbox_ip, password=args.fritzbox_password)
    logging.info("Established connection to Fritzbox")

timestamp = None
logging.info("Listening for codes on GPIO " + str(args.gpio))
while True:
    if rfdevice.rx_code_timestamp != timestamp:
        timestamp = rfdevice.rx_code_timestamp
        logging.info(str(rfdevice.rx_code) +
                     " [pulselength " + str(rfdevice.rx_pulselength) +
                     ", protocol " + str(rfdevice.rx_proto) + "]")
        if(str(rfdevice.rx_code) == args.code):
            logging.info("Sending RestAPI Post to Openhab")
            resp = requests.post(item_address, data='ON', headers={'Content-Type': 'text/plain', 'Accept': 'application/json'})
            if fritz_connection:
                arg = {'NewX_AVM-DE_PhoneNumber': args.fritzbox_phone}
                fritz_connection.call_action('X_VoIP1', 'X_AVM-DE_DialNumber', arguments=arg)
                time.sleep(5)
                fritz_connection.call_action('X_VoIP1', 'X_AVM-DE_DialHangup')
    time.sleep(0.01)
rfdevice.cleanup()
Ejemplo n.º 21
0
class FritzBoxTools:
    """FrtizBoxTools class."""
    def __init__(
        self,
        hass: HomeAssistant,
        password: str,
        username: str = DEFAULT_USERNAME,
        host: str = DEFAULT_HOST,
        port: int = DEFAULT_PORT,
    ) -> None:
        """Initialize FritzboxTools class."""
        self._cancel_scan: CALLBACK_TYPE | None = None
        self._devices: dict[str, Any] = {}
        self._options: MappingProxyType[str, Any] | None = None
        self._unique_id: str | None = None
        self.connection: FritzConnection = None
        self.fritz_hosts: FritzHosts = None
        self.fritz_profiles: dict[str, FritzProfileSwitch] = {}
        self.fritz_status: FritzStatus = None
        self.hass = hass
        self.host = host
        self.password = password
        self.port = port
        self.username = username
        self._mac: str | None = None
        self._model: str | None = None
        self._sw_version: str | None = None

    async def async_setup(self) -> None:
        """Wrap up FritzboxTools class setup."""
        await self.hass.async_add_executor_job(self.setup)

    def setup(self) -> None:
        """Set up FritzboxTools class."""
        self.connection = FritzConnection(
            address=self.host,
            port=self.port,
            user=self.username,
            password=self.password,
            timeout=60.0,
        )

        self.fritz_status = FritzStatus(fc=self.connection)
        info = self.connection.call_action("DeviceInfo:1", "GetInfo")
        if not self._unique_id:
            self._unique_id = info["NewSerialNumber"]

        self._model = info.get("NewModelName")
        self._sw_version = info.get("NewSoftwareVersion")

        self.fritz_profiles = {
            profile: FritzProfileSwitch("http://" + self.host, self.username,
                                        self.password, profile)
            for profile in get_all_profiles(self.host, self.username,
                                            self.password)
        }

    async def async_start(self, options: MappingProxyType[str, Any]) -> None:
        """Start FritzHosts connection."""
        self.fritz_hosts = FritzHosts(fc=self.connection)
        self._options = options
        await self.hass.async_add_executor_job(self.scan_devices)

        self._cancel_scan = async_track_time_interval(
            self.hass, self.scan_devices,
            timedelta(seconds=TRACKER_SCAN_INTERVAL))

    @callback
    def async_unload(self) -> None:
        """Unload FritzboxTools class."""
        _LOGGER.debug("Unloading FRITZ!Box router integration")
        if self._cancel_scan is not None:
            self._cancel_scan()
            self._cancel_scan = None

    @property
    def unique_id(self) -> str:
        """Return unique id."""
        if not self._unique_id:
            raise ClassSetupMissing()
        return self._unique_id

    @property
    def model(self) -> str:
        """Return device model."""
        if not self._model:
            raise ClassSetupMissing()
        return self._model

    @property
    def sw_version(self) -> str:
        """Return SW version."""
        if not self._sw_version:
            raise ClassSetupMissing()
        return self._sw_version

    @property
    def mac(self) -> str:
        """Return device Mac address."""
        if not self._unique_id:
            raise ClassSetupMissing()
        return self._unique_id

    @property
    def devices(self) -> dict[str, Any]:
        """Return devices."""
        return self._devices

    @property
    def signal_device_new(self) -> str:
        """Event specific per FRITZ!Box entry to signal new device."""
        return f"{DOMAIN}-device-new-{self._unique_id}"

    @property
    def signal_device_update(self) -> str:
        """Event specific per FRITZ!Box entry to signal updates in devices."""
        return f"{DOMAIN}-device-update-{self._unique_id}"

    def _update_info(self) -> list[HostInfo]:
        """Retrieve latest information from the FRITZ!Box."""
        return self.fritz_hosts.get_hosts_info()

    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 service_fritzbox(self, service: str) -> None:
        """Define FRITZ!Box services."""
        _LOGGER.debug("FRITZ!Box router: %s", service)

        if not self.connection:
            raise HomeAssistantError("Unable to establish a connection")

        try:
            if service == SERVICE_REBOOT:
                await self.hass.async_add_executor_job(
                    self.connection.call_action, "DeviceConfig1", "Reboot")
            elif service == SERVICE_RECONNECT:
                await self.hass.async_add_executor_job(
                    self.connection.call_action,
                    "WANIPConn1",
                    "ForceTermination",
                )
        except (FritzServiceError, FritzActionError) as ex:
            raise HomeAssistantError("Service or parameter unknown") from ex
        except FritzConnectionException as ex:
            raise HomeAssistantError("Service not supported") from ex
Ejemplo n.º 22
0
class FritzBoxTools(object):
    """
    Attention: The initialization of the class performs sync I/O. If you're calling this from within Home Assistant,
    wrap it in await self.hass.async_add_executor_job(lambda: FritzBoxTools(...))
    """
    def __init__(
        self,
        password,
        username=DEFAULT_USERNAME,
        host=DEFAULT_HOST,
        port=DEFAULT_PORT,
        profile_list=DEFAULT_PROFILES,
        use_port=DEFAULT_USE_PORT,
        use_deflections=DEFAULT_USE_DEFLECTIONS,
        use_wifi=DEFAULT_USE_WIFI,
        use_profiles=DEFAULT_USE_PROFILES,
    ):
        # pylint: disable=import-error
        from fritzconnection import FritzConnection
        from fritzconnection.lib.fritzstatus import FritzStatus
        from fritzprofiles import FritzProfileSwitch
        from fritzconnection.core.exceptions import FritzConnectionException

        # general timeout for all requests to the router. Some calls need quite some time.

        try:
            self.connection = FritzConnection(address=host,
                                              port=port,
                                              user=username,
                                              password=password,
                                              timeout=60.0)
            if profile_list != DEFAULT_PROFILES:
                self.profile_switch = {
                    profile: FritzProfileSwitch("http://" + host, username,
                                                password, profile)
                    for profile in profile_list
                }
            else:
                self.profile_switch = {}

            self.fritzstatus = FritzStatus(fc=self.connection)
            self._unique_id = self.connection.call_action(
                "DeviceInfo:1", "GetInfo")["NewSerialNumber"]
            self._device_info = self._fetch_device_info()
            self.success = True
            self.error = False
        except FritzConnectionException:
            self.success = False
            self.error = "connection_error"
        except PermissionError:
            self.success = False
            self.error = "connection_error_profiles"
        except AttributeError:
            self.success = False
            self.error = "profile_not_found"

        self.ha_ip = get_local_ip()
        self.profile_list = profile_list

        self.username = username
        self.password = password
        self.port = port
        self.host = host

        self.use_wifi = use_wifi
        self.use_port = use_port
        self.use_deflections = use_deflections
        self.use_profiles = use_profiles

    def service_reconnect_fritzbox(self) -> None:
        _LOGGER.info("Reconnecting the fritzbox.")
        self.connection.reconnect()

    def service_reboot_fritzbox(self) -> None:
        _LOGGER.info("Rebooting the fritzbox.")
        self.connection.call_action("DeviceConfig1", "Reboot")

    def is_ok(self):
        return self.success, self.error

    @property
    def unique_id(self):
        return self._unique_id

    @property
    def fritzbox_model(self):
        return self._device_info["model"].replace("FRITZ!Box ", "")

    @property
    def device_info(self):
        return self._device_info

    def _fetch_device_info(self):
        info = self.connection.call_action("DeviceInfo:1", "GetInfo")
        return {
            "identifiers": {
                # Serial numbers are unique identifiers within a specific domain
                (DOMAIN, self.unique_id)
            },
            "name": info.get("NewModelName"),
            "manufacturer": "AVM",
            "model": info.get("NewModelName"),
            "sw_version": info.get("NewSoftwareVersion"),
        }
Ejemplo n.º 23
0
import datetime
import setproctitle
setproctitle.setproctitle('py3-publishFritzbox')




path = (os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
with open(path+'/config.yaml') as f:
  dataMap = yaml.safe_load(f)

auth = {'username':dataMap["mqtt"]["user"], 'password':dataMap["mqtt"]["password"]}
fc = FritzConnection(dataMap["fritzBoxMain"]["ip"],password=dataMap["fritzBoxMain"]["password"])

for info in dataMap["fritzBoxMain"]["switches"]:
  wlan = str(  int( fc.call_action(info["command"], 'GetInfo')["NewEnable"] )  )
  publish.single(info["getTopic"], payload=wlan, hostname=dataMap["mqtt"]["serverIP"], auth=auth)

"""
state = fc.call_action('WLANConfiguration2', 'GetInfo')
wlan2 = state["NewEnable"]
state = fc.call_action('WLANConfiguration3', 'GetInfo')
wlan3 = state["NewEnable"]
#fc.call_action('WLANConfiguration1', 'SetEnable', NewEnable=1)
#fc.call_action('WLANConfiguration2', 'SetEnable', NewEnable=1)



publish.single(dataMap["fritzBoxMain"]["wlan2"],
  payload=str(int(wlan2)),
  hostname=dataMap["mqtt"]["serverIP"],
Ejemplo n.º 24
0
class Fritzbox:
    def __init__(self, ip, user, password):
        self.connection = FritzConnection(address=ip,
                                          user=user,
                                          password=password)

    def get_call_history(self):
        # get the URL to the xml file with all calls
        calllisturl = self.connection.call_action(
            'X_AVM-DE_OnTel', 'GetCallList')['NewCallListURL']
        # downlod xml file, parse it and get root node
        root = etree.parse(calllisturl).getroot()
        # list of all new/unprocessed incoming calls
        callerList = []
        for call in root.iter("Call"):  # iterate through calls
            # new incoming call?
            if self.get_xml_child(call, 'Type') not in ["3", "11"]:
                callerNumber = self.get_xml_child(
                    call, 'Caller')  # get caller-number
                callDate = self.get_xml_child(call, 'Date')  # get date
                callDuration = self.get_xml_child(call,
                                                  'Duration')  # get duration
                callerName = self.get_xml_child(
                    call, 'Name')  # get name from addressbook
                callId = int(self.get_xml_child(call, 'Id'))
                newcall = {
                    'id': callId,
                    'number': callerNumber,
                    'date': callDate,
                    'duration': callDuration,
                    'name': callerName
                }
                callerList.append(newcall)
        return callerList

    def get_xml_child(self, parent, attrname):
        """returns None if no child with that name or if the first is empty"""
        allattrs = parent.findall('.//' + attrname)
        if len(allattrs) > 0:
            return allattrs[0].text
        else:
            return None

    @staticmethod
    def telefonbuch_reverse_lookup(self, phonenumber):
        # now try to find that number with an reverse lookup
        try:
            dt_url = 'http://www.dastelefonbuch.de/R%C3%BCckw%C3%A4rts-Suche/'
            url = dt_url + phonenumber
            with urllib.request.urlopen(url) as response:
                START = '<div class="name" title="'
                STOP = '">'
                htmlContent = str(response.read().decode('utf-8'))
                posA = htmlContent.find(START) + len(
                    START)  # find start of name
                posB = htmlContent.find(STOP, posA)  # find end of name
                # print("dast len: ",len(START),"posA: ",posA," posB",posB)
                if posA - len(START) < 0 or posB == -1:
                    raise Exception('Not found in dastelefonbuch')
                name = htmlContent[posA:posB]  # get name
                name = name[:min(len(name), 10)]
                # we unescape html escape sequences like &ouml etc.
                return html.unescape(str(name))
        except:
            pass
Ejemplo n.º 25
0
from fritzconnection import FritzConnection
import telepot
import re

connection = FritzConnection(password='******')
ip = connection.call_action('WANPPPConnection', 'GetInfo')['NewExternalIPAddress']

match = re.search('^100\.', ip)

if match:
    bot = telepot.Bot('TELEGRAM_BOT_TOKEN');
    bot.sendMessage('@TELEGRAM_GROUP', 'I\'ve got IP: %s and I don\'t like it.' % ip)
    connection.reconnect()
Ejemplo n.º 26
0
def reboot(p):
    print ('reboot FritzBox: ', time.strftime('%X %x'))
    if p.executeReboot == True:
        from fritzconnection import FritzConnection
        connection = FritzConnection(password=p.password)
        connection.call_action('DeviceConfig', 'Reboot')
Ejemplo n.º 27
0
if elasticsearch_username is not None and elasticsearch_password is not None:
    elasticsearch_httpauth = [elasticsearch_username, elasticsearch_password]
else:
    elasticsearch_httpauth = None

es_client = Elasticsearch(hosts=os.getenv("ES_HOST", "elasticsearch"),
                          http_auth=elasticsearch_httpauth)
fritzbox_client = FritzConnection(address=os.getenv("FRITZ_HOST"))

index_prefix = os.getenv("ES_INDEX_PREFIX", "fritzbox")
index_date_format = os.getenv("ES_INDEX_DATE_FORMAT")

log_regex = re.compile(r"^([0-9\. :]+) (.*)$")

for line in fritzbox_client.call_action(
        "DeviceInfo1", "GetDeviceLog")["NewDeviceLog"].split("\n"):
    match = log_regex.match(line)

    date_time = match.group(1)
    message = match.group(2)

    date = datetime.strptime(date_time, "%d.%m.%y %H:%M:%S")
    date = timezone.localize(date)

    doc_id = hashlib.sha1(line.encode("utf-8")).hexdigest()

    if index_date_format is None:
        index_name = index_prefix
    else:
        index_name = "-".join([index_prefix, date.strftime(index_date_format)])
Ejemplo n.º 28
0
class CheckCallList:
    def __init__(self,
                 address='192.168.178.1',
                 user='******',
                 password='******',
                 knownCallId=0):
        """
        Parameters
        ----------
            address : str
                IP of FritzBox
            user : str
                FritzBox credential
            password : str
                FritzBox credential
            knownCallId : str | int
                ID of the last processed call (process only newer)
        """
        self.CHECKCALLLIST_INITDATA = {
            'address': address,
            'password': password,
            'user': user,
            'knownCallId': knownCallId
        }
        self.connection = FritzConnection(address=address,
                                          user=user,
                                          password=password)
        self.knownCallId = int(knownCallId)

    def getConfig(self):
        self.CHECKCALLLIST_INITDATA['knownCallId'] = self.knownCallId
        return {
            'phonebook': Phonebook.phonebook,
            'CHECKCALLLIST_INITDATA': self.CHECKCALLLIST_INITDATA
        }

    @staticmethod
    def loadFromConfig(data, use_mock=False):
        """
        Parameters
        ----------
            use_mock : boolean
                If true, will return CheckCallListMock instead of CheckCallList for testing.
        """
        Phonebook.phonebook = data['phonebook']

        if use_mock:
            return CheckCallListMock(data['CHECKCALLLIST_INITDATA'])

        return CheckCallList(**data['CHECKCALLLIST_INITDATA'])

    def checkForNewCalls(self, knownCallId=None) -> List[Call]:
        """
        Parameters
        ----------
            knownCallId : int
                Index of last call already processed. Will skip all previous calls.
                If set to `None` only new calls since the last invocation are be returned.
        """
        if knownCallId is not None:
            self.knownCallId = knownCallId
        # get the URL to the xml file with all calls
        calllisturl = self.connection.call_action(
            'X_AVM-DE_OnTel', 'GetCallList')['NewCallListURL']
        # print(calllisturl)
        # downlod xml file, parse it and get root node
        root = etree.parse(calllisturl).getroot()

        def getChild(parent, attrname):
            """returns None if no child with that name or if the first is empty"""
            allattrs = parent.findall('.//' + attrname)
            if len(allattrs) > 0:
                return allattrs[0].text
            else:
                return None

        # list of all new/unprocessed incoming calls
        callerList = []
        # will later overwrite knownCallId
        maxKnownCallId = self.knownCallId
        for call in root.iter("Call"):  # iterate through calls
            # new incoming call?
            callId = int(getChild(call, 'Id'))
            if callId <= self.knownCallId:
                break  # that's not a new call
            if getChild(call, 'Type') not in ["3", "11"]:
                print("max", maxKnownCallId, "callid", callId)
                maxKnownCallId = max(maxKnownCallId, callId)  # save highest id
                callerNumber = getChild(call, 'Caller')  # get caller-number
                callDate = getChild(call, 'Date')  # get date
                callDuration = getChild(call, 'Duration')  # get duration
                callerName = None  #getChild(call, 'Name') # get name from addressbook

                # print(str(callChild(call, 'Name')) + " --> "+str(callerName))
                newcall = Call(callerNumber, callDate, callDuration,
                               callerName)
                print("new call id(" + str(callId) + "): " + str(newcall))
                callerList.append(newcall)
        self.knownCallId = maxKnownCallId
        return callerList
Ejemplo n.º 29
0
class FritzBoxTools(update_coordinator.DataUpdateCoordinator):
    """FritzBoxTools class."""

    def __init__(
        self,
        hass: HomeAssistant,
        password: str,
        username: str = DEFAULT_USERNAME,
        host: str = DEFAULT_HOST,
        port: int = DEFAULT_PORT,
    ) -> None:
        """Initialize FritzboxTools class."""
        super().__init__(
            hass=hass,
            logger=_LOGGER,
            name=f"{DOMAIN}-{host}-coordinator",
            update_interval=timedelta(seconds=30),
        )

        self._devices: dict[str, FritzDevice] = {}
        self._options: MappingProxyType[str, Any] | None = None
        self._unique_id: str | None = None
        self.connection: FritzConnection = None
        self.fritz_hosts: FritzHosts = None
        self.fritz_status: FritzStatus = None
        self.hass = hass
        self.host = host
        self.mesh_role = MeshRoles.NONE
        self.device_is_router: bool = True
        self.password = password
        self.port = port
        self.username = username
        self._model: str | None = None
        self._current_firmware: str | None = None
        self._latest_firmware: str | None = None
        self._update_available: bool = False

    async def async_setup(
        self, options: MappingProxyType[str, Any] | None = None
    ) -> None:
        """Wrap up FritzboxTools class setup."""
        self._options = options
        await self.hass.async_add_executor_job(self.setup)

    def setup(self) -> None:
        """Set up FritzboxTools class."""
        self.connection = FritzConnection(
            address=self.host,
            port=self.port,
            user=self.username,
            password=self.password,
            timeout=60.0,
            pool_maxsize=30,
        )

        if not self.connection:
            _LOGGER.error("Unable to establish a connection with %s", self.host)
            return

        self.fritz_hosts = FritzHosts(fc=self.connection)
        self.fritz_status = FritzStatus(fc=self.connection)
        info = self.connection.call_action("DeviceInfo:1", "GetInfo")
        if not self._unique_id:
            self._unique_id = info["NewSerialNumber"]

        self._model = info.get("NewModelName")
        self._current_firmware = info.get("NewSoftwareVersion")

        self._update_available, self._latest_firmware = self._update_device_info()
        self.device_is_router = "WANIPConn1" in self.connection.services

    @callback
    async def _async_update_data(self) -> None:
        """Update FritzboxTools data."""
        try:
            await self.async_scan_devices()
        except (FritzSecurityError, FritzConnectionException) as ex:
            raise update_coordinator.UpdateFailed from ex

    @property
    def unique_id(self) -> str:
        """Return unique id."""
        if not self._unique_id:
            raise ClassSetupMissing()
        return self._unique_id

    @property
    def model(self) -> str:
        """Return device model."""
        if not self._model:
            raise ClassSetupMissing()
        return self._model

    @property
    def current_firmware(self) -> str:
        """Return current SW version."""
        if not self._current_firmware:
            raise ClassSetupMissing()
        return self._current_firmware

    @property
    def latest_firmware(self) -> str | None:
        """Return latest SW version."""
        return self._latest_firmware

    @property
    def update_available(self) -> bool:
        """Return if new SW version is available."""
        return self._update_available

    @property
    def mac(self) -> str:
        """Return device Mac address."""
        if not self._unique_id:
            raise ClassSetupMissing()
        return dr.format_mac(self._unique_id)

    @property
    def devices(self) -> dict[str, FritzDevice]:
        """Return devices."""
        return self._devices

    @property
    def signal_device_new(self) -> str:
        """Event specific per FRITZ!Box entry to signal new device."""
        return f"{DOMAIN}-device-new-{self._unique_id}"

    @property
    def signal_device_update(self) -> str:
        """Event specific per FRITZ!Box entry to signal updates in devices."""
        return f"{DOMAIN}-device-update-{self._unique_id}"

    def _update_hosts_info(self) -> list[HostInfo]:
        """Retrieve latest hosts information from the FRITZ!Box."""
        try:
            return self.fritz_hosts.get_hosts_info()  # type: ignore [no-any-return]
        except Exception as ex:  # pylint: disable=[broad-except]
            if not self.hass.is_stopping:
                raise HomeAssistantError("Error refreshing hosts info") from ex
        return []

    def _update_device_info(self) -> tuple[bool, str | None]:
        """Retrieve latest device information from the FRITZ!Box."""
        version = self.connection.call_action("UserInterface1", "GetInfo").get(
            "NewX_AVM-DE_Version"
        )
        return bool(version), version

    async def async_scan_devices(self, now: datetime | None = None) -> None:
        """Wrap up FritzboxTools class scan."""
        await self.hass.async_add_executor_job(self.scan_devices, now)

    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 device %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 device %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 dr.format_mac(int_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)

    async def async_trigger_firmware_update(self) -> bool:
        """Trigger firmware update."""
        results = await self.hass.async_add_executor_job(
            self.connection.call_action, "UserInterface:1", "X_AVM-DE_DoUpdate"
        )
        return cast(bool, results["NewX_AVM-DE_UpdateState"])

    async def async_trigger_reboot(self) -> None:
        """Trigger device reboot."""
        await self.hass.async_add_executor_job(self.connection.reboot)

    async def async_trigger_reconnect(self) -> None:
        """Trigger device reconnect."""
        await self.hass.async_add_executor_job(self.connection.reconnect)

    async def async_trigger_cleanup(
        self, config_entry: ConfigEntry | None = None
    ) -> None:
        """Trigger device trackers cleanup."""
        device_hosts_list = await self.hass.async_add_executor_job(
            self.fritz_hosts.get_hosts_info
        )
        entity_reg: er.EntityRegistry = er.async_get(self.hass)

        if config_entry is None:
            if self.config_entry is None:
                return
            config_entry = self.config_entry

        ha_entity_reg_list: list[er.RegistryEntry] = er.async_entries_for_config_entry(
            entity_reg, config_entry.entry_id
        )
        entities_removed: bool = False

        device_hosts_macs = set()
        device_hosts_names = set()
        for device in device_hosts_list:
            device_hosts_macs.add(device["mac"])
            device_hosts_names.add(device["name"])

        for entry in ha_entity_reg_list:
            if entry.original_name is None:
                continue
            entry_name = entry.name or entry.original_name
            entry_host = entry_name.split(" ")[0]
            entry_mac = entry.unique_id.split("_")[0]

            if not _cleanup_entity_filter(entry) or (
                entry_mac in device_hosts_macs and entry_host in device_hosts_names
            ):
                _LOGGER.debug(
                    "Skipping entity %s [mac=%s, host=%s]",
                    entry_name,
                    entry_mac,
                    entry_host,
                )
                continue
            _LOGGER.info("Removing entity: %s", entry_name)
            entity_reg.async_remove(entry.entity_id)
            entities_removed = True

        if entities_removed:
            self._async_remove_empty_devices(entity_reg, config_entry)

    @callback
    def _async_remove_empty_devices(
        self, entity_reg: er.EntityRegistry, config_entry: ConfigEntry
    ) -> None:
        """Remove devices with no entities."""

        device_reg = dr.async_get(self.hass)
        device_list = dr.async_entries_for_config_entry(
            device_reg, config_entry.entry_id
        )
        for device_entry in device_list:
            if not er.async_entries_for_device(
                entity_reg,
                device_entry.id,
                include_disabled_entities=True,
            ):
                _LOGGER.info("Removing device: %s", device_entry.name)
                device_reg.async_remove_device(device_entry.id)

    async def service_fritzbox(
        self, service_call: ServiceCall, config_entry: ConfigEntry
    ) -> None:
        """Define FRITZ!Box services."""
        _LOGGER.debug("FRITZ!Box service: %s", service_call.service)

        if not self.connection:
            raise HomeAssistantError("Unable to establish a connection")

        try:
            if service_call.service == SERVICE_REBOOT:
                _LOGGER.warning(
                    'Service "fritz.reboot" is deprecated, please use the corresponding button entity instead'
                )
                await self.async_trigger_reboot()
                return

            if service_call.service == SERVICE_RECONNECT:
                _LOGGER.warning(
                    'Service "fritz.reconnect" is deprecated, please use the corresponding button entity instead'
                )
                await self.async_trigger_reconnect()
                return

            if service_call.service == SERVICE_CLEANUP:
                _LOGGER.warning(
                    'Service "fritz.cleanup" is deprecated, please use the corresponding button entity instead'
                )
                await self.async_trigger_cleanup(config_entry)
                return

        except (FritzServiceError, FritzActionError) as ex:
            raise HomeAssistantError("Service or parameter unknown") from ex
        except FritzConnectionException as ex:
            raise HomeAssistantError("Service not supported") from ex
Ejemplo n.º 30
0
class FritzBoxTools:
    """FrtizBoxTools class."""
    def __init__(
        self,
        hass,
        password,
        username=DEFAULT_USERNAME,
        host=DEFAULT_HOST,
        port=DEFAULT_PORT,
    ):
        """Initialize FritzboxTools class."""
        self._cancel_scan = None
        self._device_info = None
        self._devices: dict[str, Any] = {}
        self._unique_id = None
        self.connection = None
        self.fritzhosts = None
        self.fritzstatus = None
        self.hass = hass
        self.host = host
        self.password = password
        self.port = port
        self.username = username

    async def async_setup(self):
        """Wrap up FritzboxTools class setup."""
        return await self.hass.async_add_executor_job(self.setup)

    def setup(self):
        """Set up FritzboxTools class."""
        self.connection = FritzConnection(
            address=self.host,
            port=self.port,
            user=self.username,
            password=self.password,
            timeout=60.0,
        )

        self.fritzstatus = FritzStatus(fc=self.connection)
        if self._unique_id is None:
            self._unique_id = self.connection.call_action(
                "DeviceInfo:1", "GetInfo")["NewSerialNumber"]

        self._device_info = self._fetch_device_info()

    async def async_start(self):
        """Start FritzHosts connection."""
        self.fritzhosts = FritzHosts(fc=self.connection)

        await self.hass.async_add_executor_job(self.scan_devices)

        self._cancel_scan = async_track_time_interval(
            self.hass, self.scan_devices,
            timedelta(seconds=TRACKER_SCAN_INTERVAL))

    @callback
    def async_unload(self):
        """Unload FritzboxTools class."""
        _LOGGER.debug("Unloading FRITZ!Box router integration")
        if self._cancel_scan is not None:
            self._cancel_scan()
            self._cancel_scan = None

    @property
    def unique_id(self):
        """Return unique id."""
        return self._unique_id

    @property
    def fritzbox_model(self):
        """Return model."""
        return self._device_info["model"].replace("FRITZ!Box ", "")

    @property
    def device_info(self):
        """Return device info."""
        return self._device_info

    @property
    def devices(self) -> dict[str, Any]:
        """Return devices."""
        return self._devices

    @property
    def signal_device_new(self) -> str:
        """Event specific per FRITZ!Box entry to signal new device."""
        return f"{DOMAIN}-device-new-{self._unique_id}"

    @property
    def signal_device_update(self) -> str:
        """Event specific per FRITZ!Box entry to signal updates in devices."""
        return f"{DOMAIN}-device-update-{self._unique_id}"

    def _update_info(self):
        """Retrieve latest information from the FRITZ!Box."""
        return self.fritzhosts.get_hosts_info()

    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)

        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)
            else:
                device = FritzDevice(dev_mac)
                device.update(dev_info, dev_home)
                self._devices[dev_mac] = device
                new_device = True

        async_dispatcher_send(self.hass, self.signal_device_update)
        if new_device:
            async_dispatcher_send(self.hass, self.signal_device_new)

    def _fetch_device_info(self):
        """Fetch device info."""
        info = self.connection.call_action("DeviceInfo:1", "GetInfo")

        dev_info = {}
        dev_info["identifiers"] = {
            # Serial numbers are unique identifiers within a specific domain
            (DOMAIN, self.unique_id)
        }
        dev_info["manufacturer"] = "AVM"

        if dev_name := info.get("NewName"):
            dev_info["name"] = dev_name
        if dev_model := info.get("NewModelName"):
            dev_info["model"] = dev_model
Ejemplo n.º 31
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from fritzconnection import FritzConnection

connection = FritzConnection()

status = connection.call_action('WANCommonInterfaceConfig', 'GetCommonLinkProperties')
print(status['NewLayer1DownstreamMaxBitRate'])
print(status['NewLayer1UpstreamMaxBitRate'])
Ejemplo n.º 32
0
class FritzBoxTools(object):
    """
    Attention: The initialization of the class performs sync I/O. If you're calling this from within Home Assistant,
    wrap it in await self.hass.async_add_executor_job(lambda: FritzBoxTools(...))
    """
    def __init__(
        self,
        password,
        username = DEFAULT_USERNAME,
        host = DEFAULT_HOST,
        port=DEFAULT_PORT,
        profile_on = DEFAULT_PROFILE_ON,
        profile_off = DEFAULT_PROFILE_OFF,
        device_list = DEFAULT_DEVICES,
        use_port = DEFAULT_USE_PORT,
        use_deflections = DEFAULT_USE_DEFLECTIONS,
        use_wifi = DEFAULT_USE_WIFI,
        use_devices = DEFAULT_USE_DEVICES,
    ):
        # pylint: disable=import-error
        from fritzconnection import FritzConnection
        from fritzconnection.lib.fritzstatus import FritzStatus
        from fritz_switch_profiles import FritzProfileSwitch

        # general timeout for all requests to the router. Some calls need quite some time.
        self.connection = FritzConnection(
            address=host, port=port, user=username, password=password, timeout=30.0
        )

        if device_list != DEFAULT_DEVICES:
            self.profile_switch = FritzProfileSwitch(
                "http://" + host, username, password
            )

        self.fritzstatus = FritzStatus(fc=self.connection)
        self.ha_ip = get_local_ip()
        self.profile_on = profile_on
        self.profile_off = profile_off
        self.profile_last_updated = time.time()
        self.device_list = device_list

        self.username = username
        self.password = password
        self.port = port
        self.host = host

        self.use_wifi = use_wifi
        self.use_port = use_port
        self.use_deflections = use_deflections
        self.use_devices = use_devices

        self._unique_id = self.connection.call_action("DeviceInfo:1", "GetInfo")[
            "NewSerialNumber"
        ]
        self._device_info = self._fetch_device_info()

    async def async_update_profiles(self, hass):
        if time.time() > self.profile_last_updated + 5:
            # do not update profiles too often (takes too long...)!
            await hass.async_add_executor_job(self.profile_switch.fetch_profiles)
            await hass.async_add_executor_job(self.profile_switch.fetch_devices)
            await hass.async_add_executor_job(self.profile_switch.fetch_device_profiles)
            self.profile_last_updated = time.time()

    def service_reconnect_fritzbox(self, call) -> None:
        _LOGGER.info("Reconnecting the fritzbox.")
        self.connection.reconnect()

    def is_ok(self):
        # TODO for future: do more of the async_setup_entry checks right here

        from fritzconnection.core.exceptions import FritzConnectionException

        try:
            _ = self.connection.call_action(
                "Layer3Forwarding:1", "GetDefaultConnectionService"
            )["NewDefaultConnectionService"]
            return True, ""
        except FritzConnectionException:
            return False, "connection_error"

    @property
    def unique_id(self):
        return self._unique_id

    @property
    def device_info(self):
        return self._device_info

    def _fetch_device_info(self):
        info = self.connection.call_action("DeviceInfo:1", "GetInfo")
        return {
            "identifiers": {
                # Serial numbers are unique identifiers within a specific domain
                (DOMAIN, self.unique_id)
            },
            "name": info.get("NewProductClass"),
            "manufacturer": "AVM",
            "model": info.get("NewModelName"),
            "sw_version": info.get("NewSoftwareVersion"),
        }