Source code for BAC0.core.functions.GetIPAddr

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 by Christian Tremblay, P.Eng <christian.tremblay@servisys.com>
#
# Licensed under LGPLv3, see file LICENSE in this source tree.
"""
Utility function to retrieve a functionnal IP and a correct broadcast IP
address.
Goal : not use 255.255.255.255 as a broadcast IP address as it is not
accepted by every devices (>3.8.38.1 bacnet.jar of Tridium Jace for example)

"""
import ipaddress
import socket
import typing as t

from bacpypes.pdu import Address

from ...core.utils.notes import note_and_log
from ..io.IOExceptions import NetworkInterfaceException

DEFAULT_PORT = 47808


[docs]@note_and_log class HostIP: """ Special class to identify host IP informations """ def __init__(self, port: t.Optional[int] = None) -> None: ip = self._findIPAddr() mask = self._findSubnetMask(ip) if port is not None: self._port = port else: self._port = DEFAULT_PORT self.interface = ipaddress.IPv4Interface("{}/{}".format(ip, mask)) @property def ip_address_subnet(self): """ IP Address/subnet """ return "{}/{}".format( self.interface.ip.compressed, self.interface.exploded.split("/")[-1] ) @property def ip_address(self): """ IP Address/subnet """ return "{}".format(self.interface.ip.compressed) @property def address(self) -> Address: """ IP Address using bacpypes Address format """ port = "" if self._port: port = ":{}".format(self._port) return Address( "{}/{}{}".format( self.interface.ip.compressed, self.interface.exploded.split("/")[-1], port, ) ) @property def mask(self): """ Subnet mask """ return self.interface.exploded.split("/")[-1] @property def port(self): """ IP Port used """ return self._port def _findIPAddr(self) -> str: """ Retrieve the IP address connected to internet... used as a default IP address when defining Script :returns: IP Adress as String """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(("google.com", 443)) addr = s.getsockname()[0] s.close() except socket.error: raise NetworkInterfaceException( "Impossible to retrieve IP, please provide one manually" ) return addr def _findSubnetMask(self, ip: str) -> str: """ Retrieve the broadcast IP address connected to internet... used as a default IP address when defining Script :param ip: (str) optionnal IP address. If not provided, default to getIPAddr() :returns: broadcast IP Adress as String """ try: import netifaces interfaces = netifaces.interfaces() for nic in interfaces: addresses = netifaces.ifaddresses(nic) try: for address in addresses[netifaces.AF_INET]: if address["addr"] == ip: return address["netmask"] except KeyError: pass return "255.255.255.255" except ImportError: self._log.warning( "Netifaces not installed on your system. BAC0 can't detect the subnet.\nPlease provide subnet for now, " "we'll consider 255.255.255.0 (/24).\nYou can install netifaces using 'pip install netifaces'." ) return "255.255.255.0"
[docs]def validate_ip_address(ip: Address) -> bool: result = True s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: if not isinstance(ip, Address): raise ValueError("Provide Address as bacpypes.Address object") s.bind(ip.addrTuple) except OSError: result = False finally: s.close() return result