In my project, I need to find a camera device that uses the onvif.
I get the desired results through multicast-socket, but it's not as expected.
This is my code:
my_ip = "192.168.1.88"
mul_ip = "239.255.255.250"
mul_port = 37020
xml_str = '<?xml version="1.0" encoding="utf-8"?><Envelope xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns="http://www.w3.org/2003/05/soap-envelope"><Header><wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">uuid:05ebeac7-ab22-4f3c-ac10-71a6bed6788b</wsa:MessageID><wsa:To xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To><wsa:Action xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action></Header><Body><Probe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery"><Types>tds:Device</Types><Scopes /></Probe></Body></Envelope><?xml version="1.0" encoding="utf-8"?><Envelope xmlns:dn="http://www.onvif.org/ver10/network/wsdl" xmlns="http://www.w3.org/2003/05/soap-envelope"><Header><wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">uuid:6604dcab-ae78-4b39-86af-71a3c71884a6</wsa:MessageID><wsa:To xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To><wsa:Action xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action></Header><Body><Probe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery"><Types>dn:NetworkVideoTransmitter</Types><Scopes /></Probe></Body></Envelope>'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
s.bind((my_ip, mul_port))
s.setsockopt(
socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(mul_ip) + socket.inet_aton(my_ip)
)
s.setblocking(False)
s.sendto(xml_str.encode(), (mul_ip, mul_port))
while True:
try:
data, address = s.recvfrom(65535)
print('address: ', address)
print('data: ', data)
except Exception as e:
pass
This result returns the original xml_str input and my own IP/PORT(my_ip mul_port).
I want to understand two issues:
Is there anything wrong with this python multicast-socket?
Are there other ways to get onvif devices? I used python-ws-discovery (https://github.com/andreikop/python-ws-discovery), but it doesn't work
on Windows10 OS.
I cannot answer the first questions since I'm not that familiar with it multi-socket,
but I can help with the second one.
The code I'm using to discover ONVIF devices in my network (on Windows 10 and macOS)
is as follow:
from wsdiscovery.discovery import ThreadedWSDiscovery as WSDiscovery
from wsdiscovery import Scope
import re
def display(any_list):
for item in any_list:
print(item)
def fetch_devices():
wsd = WSDiscovery()
scope1 = Scope("onvif://www.onvif.org/Profile")
wsd.start()
services = wsd.searchServices(scopes=[scope1])
ipaddresses = []
for service in services:
#filter those devices that dont have ONVIF service
ipaddress = re.search('(\d+|\.)+', str(service.getXAddrs()[0])).group(0)
ipaddresses.append(ipaddress)
print(display(service.getScopes()))
print('----------END')
print(f'\nnumber of devices detected: {len(services)}')
wsd.stop()
return ipaddresses
if __name__ == "__main__":
onvif_devices_IPs = fetch_devices()
display(onvif_devices_IPs)
I'm using the line scope1 = Scope("onvif://www.onvif.org/Profile") to filter and display just those devices with Profiles.
Related
I'm trying to connect a LE device to my Linux laptop through a python script. Nevertheless, the device address must be specified as "random" for the connection to happen, and the examples that I have (mainly https://www.bluetooth.com/blog/the-bluetooth-for-linux-developers-study-guide/) doesn't show any way of doing it.
The device-api from BlueZ (https://github.com/bluez/bluez/blob/master/doc/device-api.txt) list it as one of its properties, but my knowledge is still incomplete, and I couldn't manage to find a way of setting this property.
Any idea, indication or example will be immensely helpful.
Following is my script
PATH_DA_BSN = "/org/bluez/hci0/dev_CA_DB_17_8A_02_97"
ADAPTER_NAME = "hci0"
BLUEZ_SERVICE_NAME = "org.bluez"
BLUEZ_NAMESPACE = "/org/bluez/"
DEVICE_INTERFACE = BLUEZ_SERVICE_NAME + ".Device1"
ADAPTER_INTERFACE = BLUEZ_SERVICE_NAME + ".Adapter1"
def connect():
global bus
global device_interface
try:
device_interface.Connect()
except Exception as e:
print("Failed to connect")
print(e.get_dbus_name())
print(e.get_dbus_message())
if ("UnknownObject" in e.get_dbus_name()):
print("Try scanning first to resolve this problem")
return bluetooth_constants.RESULT_EXCEPTION
else:
print("Connected OK")
return bluetooth_constants.RESULT_OK
bus = dbus.SystemBus()
bsn_proxy = bus.get_object(BLUEZ_SERVICE_NAME, PATH_DA_BSN)
device_interface = dbus.Interface(bsn_proxy, DEVICE_INTERFACE)
adapter_path = BLUEZ_NAMESPACE + ADAPTER_NAME
# acquire the adapter interface so we can call its methods
adapter_object = bus.get_object(BLUEZ_SERVICE_NAME, adapter_path)
adapter_interface = dbus.Interface(adapter_object, ADAPTER_INTERFACE)
print("Connecting to " + PATH_DA_BSN)
connect()
The AddressType is already set from when the device was discovered.
You can iterate through the already discovered devices using D-Bus's GetManagedObjects functionality to find what the address type is set to for each device.
An example using the pydbus bindings:
>>> import pydbus
>>> bus = pydbus.SystemBus()
>>> mngr = bus.get('org.bluez', '/')
>>> mngd_objs = mngr.GetManagedObjects()
>>> for path, iface in mngd_objs.items():
... if 'org.bluez.Device1' in iface.keys():
... print(iface.get('org.bluez.Device1', {}).get('AddressType'))
...
public
random
random
public
public
public
random
public
public
public
random
you have 2 options:
Do a discovery and connect when the device is found. In this case Bluez will already know the addresstype.
Call the method ConnectDevice which is available on an adapter. In this method you can pass the ‘random’ parameter. Keep in mind this method is marked as experimental.
I recommend option 1
Background: I want to use ble for server/client software. But in this case, the server should be the peripheral with multiple connections. For that i start multiple services, up to 10. I think that's the limit for peripheral connections on my chip. The different services are the same in functionality but should connect to only one client. I see no other possibility to differentiate the clients in the notification function.
Problem: For that i restart the advertising after each connection. In the advertisement, I set the uuid information field “ServiceUUIDs” for the next free service to connect. This is working after restarting my pc. In my flutter app, I’m able to read this field perfectly in the advertisment. But when I restart the python script, the uuid is no longer advertised and the field “ServiceUUIDs” is empty although {'Type': 'peripheral', 'ServiceUUIDs': dbus.Array(['0000ac01-0000-1000-8000-00805f9b34fb'], signature=dbus.Signature('s')), 'LocalName': dbus.String('Hello'), 'Discoverable': dbus.Boolean(True)} is set in the bluetooth_constants.ADVERTISING_MANAGER_INTERFACE. Also restarting the bluetooth module is not working. Therefore I must restart my pc everytime I want to restart my server... After connecting to the server, the client can scan all services and does not know, which one of the 10 services is free to connect.
My python code is the same as in the dbus tutorial:
class Advertisement(dbus.service.Object):
PATH_BASE = '/org/bluez/ldsg/advertisement'
def __init__(self, bus, index, advertising_type):
self.path = self.PATH_BASE + str(index)
self.bus = bus
self.ad_type = advertising_type
self.service_uuids = ['0000ac01-0000-1000-8000-00805f9b34fb']
self.manufacturer_data = None
self.solicit_uuids = None
self.service_data = None
self.local_name = 'Hello'
self.include_tx_power = False
self.data = None #{0x26: dbus.Array([0x01, 0x01, 0x01], signature='y')}
self.discoverable = True
dbus.service.Object.__init__(self, bus, self.path)
def get_properties(self):
properties = dict()
properties['Type'] = self.ad_type
if self.service_uuids is not None:
properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
signature='s')
if self.solicit_uuids is not None:
properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
signature='s')
if self.manufacturer_data is not None:
properties['ManufacturerData'] = dbus.Dictionary(
self.manufacturer_data, signature='qv')
if self.service_data is not None:
properties['ServiceData'] = dbus.Dictionary(self.service_data,
signature='sv')
if self.local_name is not None:
properties['LocalName'] = dbus.String(self.local_name)
if self.discoverable is not None and self.discoverable == True:
properties['Discoverable'] = dbus.Boolean(self.discoverable)
if self.include_tx_power:
properties['Includes'] = dbus.Array(["tx-power"], signature='s')
if self.data is not None:
properties['Data'] = dbus.Dictionary(
self.data, signature='yv')
print(properties)
return {bluetooth_constants.ADVERTISING_MANAGER_INTERFACE: properties}
def start_advertising():
global adv
global adv_mgr_interface
# we're only registering one advertisement object so index (arg2) is hard coded as 0
print("Registering advertisement",adv.get_path())
adv_mgr_interface.RegisterAdvertisement(adv.get_path(), {},
reply_handler=register_ad_cb,
error_handler=register_ad_error_cb)
bus = dbus.SystemBus()
adv_mgr_interface = dbus.Interface(bus.get_object(bluetooth_constants.BLUEZ_SERVICE_NAME,adapter_path), bluetooth_constants.ADVERTISING_MANAGER_INTERFACE)
adv = Advertisement(bus, 0, 'peripheral')
start_advertising()
Maybe there is an other way to send additional information in the advertisment? I also tried "Data" and "ServiceData" but then the advertisement throws an error. I haven't found a good tutorial for this. I'm only looking for a stable way to give the additional information for the next free service in the advertisment.
Thank You!
I am using the following function to implement a program that changes its behavior depending on the IP of the connected PC.
There is a problem with this function that if something tries to login and fails, it may get the IP of the failed one.
And now that we've encountered that possibility, the program is broken.
What edits do I need to make to make this function behave as expected?
import psutil
def get_ip(port=3389):
ip = ""
for x in psutil.net_connections():
if x.status == "ESTABLISHED" and x.laddr.port == port:
ip = x.raddr.ip
break
I changed the function based on Bijay Regmi's comment. Thank you. wmi was difficult for me, so I used win32evtlog to read it out little by little. I am working on improving readability and finding bugs little by little.
def systime(xml):
return datetime.fromisoformat(xml.find(f'{ns}System/{ns}TimeCreated').get('SystemTime')[:-2] + "+00:00")
def last_event(handle,
event_id,
condition: Callable[['Event'], bool] = None) -> Optional['Event']:
now = datetime.now(tz=timezone.utc)
while True:
events = win32evtlog.EvtNext(handle, 20)
if not events:
break
for event in events:
xml_content = win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml)
obj = Event(ET.fromstring(xml_content))
if obj.EventID == event_id:
if obj.SystemTime + timedelta(minutes=5) < now:
return None
if condition and not condition(obj):
continue
return obj
class Event:
def __init__(self, xml: ET.Element):
self.EventID = xml and xml.find(f'{ns}System/{ns}EventID').text
self.SystemTime = xml and systime(xml)
self.xml = xml
if self.EventID == '24':
self.IpAddress = xml.find(f'{ns}UserData/{{Event_NS}}EventXML/{{Event_NS}}Address').text
elif self.EventID == '4624':
self.IpAddress = xml.find(f'{ns}EventData/{ns}Data[#Name="IpAddress"]').text
else:
self.IpAddress = None
Before reading my question, my english skill is poor, so please send me feedback or advise in easy words. Thank you.
What I Want To Do
I want to build a webserver on ESP32-WROOM-32(ESP32-DevKitc_V4) with MicroPython(v1.16 on 2021-06-23: latest firmware available). My Goal is building a webserver inside ESP32 and collecting the data of sensors connected to ESP32 periodically.
Environment
Windows10(64bit)
MicroPython (v1.16 on 2021-06-23)
Editor : Thonny Editor (v3.3.11)
ESP32 Devkitc_v4
ampy (v1.1.0)
What I did
Firstly I wrote a code according to this article : RandomNerdTutorial: ESP32/ESP8266 MicroPython Web Server – Control Outputs. My code is as follows:
import network
import usys
try:
import usocket as socket
except:
import socket
def connect(SSID,PASS):
AP = network.WLAN(network.AP_IF)
if AP.isconnected():
print("Already connected.")
return AP
else:
AP.active(True)
AP.connect(SSID,PASS)
while not AP.isconnected():
print(".",end="")
utime.sleep(0.5)
if AP.isconnected():
print("Network connection is established.")
return AP
else:
print("Connection failed.")
return False
wifi = connect("myssid","mypass")
if not wifi:
usys.exit(0)
html_strings = """
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<title>SERVER TEST</title>
</head>
<body>
<h1>SERVER TEST</h1>
</body>
</html>
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
addr = socket.getaddrinfo(("0,0,0,0",80))[0][-1]
s.bind(addr)
s.listen(1)
print("Linstening on ", addr)
while True:
conn,addr = s.accept()
print("connected from : ",addr)
request = conn.recv(1024)
respose = html_strings
conn.send("HTTP/1.0 200 OK\r\n")
conn.send("Content-type: text/html\r\n")
conn.send("\r\n")
conn.send(response)
conn.close()
I connect ESP32 board with USB cable to PC and Windows10 recognize this device as COM4. I wrote this code on Thonny Editor on windows10(64bit) and then transfer this code as main.py using ampy command on command line like this:
> ampy --port COM4 put main.pye
Tansfer was always successful and then connecting ESP32 from Thonny editor. firstly this message is shown in the shell console like this:
MicroPython v1.16 on 2021-06-23; ESP32 module with ESP32
Type "help()" for more information.
And then when I press reset button on ESP32, Error was shown like this:
Traceback(most recent call last)
File "main.py", line 48, in <module>
TypeError: function mising 1 required positional arguments
The line 48 corresponds this line:
addr = socket.getaddrinfo(("0,0,0,0",80))
According to the official documentation Docs » MicroPython libraries » usocket – socket module,it seems that prividing only two parameters: hostname nad port is enough. I also tried by writing like this:
addr = socket.getaddrinfo(('',80))[#][-1]
But the result was same. I do not understand what is the required another positional argument. So I want advice or feedback to solve this issue.
Thank you for your cooperation.
The getaddrinfo method expects two arguments:
socket.getaddrinfo('hostname', port)
You are passing a single argument -- a 2-tuple. Instead of:
addr = socket.getaddrinfo(("0.0.0.0", 80))
You want:
addr = socket.getaddrinfo("0.0.0.0",80)
Note that I've removed one set of parentheses.
Here's a complete example at the REPL:
>>> import socket
>>> s = socket.socket()
>>> addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
>>> s.bind(addr)
>>> s.listen(1)
>>> conn,addr = s.accept()
>>> addr
('192.168.1.175', 43384)
>>> conn.send('HTTP/1.0 200 OK\r\n')
17
>>> conn.close()
I like to create a kind of indoor-tracking-system for my already existing home automation system. I thought of using BLE. I already successfully set up hcitool on my Raspberry Pi and I can connect to my iPhone without any problems. But how can I obtain the signal strength between my Raspberry Pi and my iPhone without connecting them. I already tried to use sudo hcitool cc [BTADDRESS] to connect to my iPhone without authentication, but it looks like the iPhone don't allow those connection to stay open. I think that must be a way to get the signal strength without connecting both devices. I want to use it to determine the distance from my Raspberry Pi to my iPhone. May I am able to calculate the distance from the time I need to discover my iPhone?
There are two ways to go, and by now I have been able to get both work reliably only on Android devices.
Exploiting the Bluetooth friendly name of the smartphone and set the discoverability to infinite. I have done this writing a simple app. Works in background, also after that the app has been killed, since the discoverability setting is preserved. At the best of my knowledge, this is not possible in iOS.
Advertising a UUID in a BLE packet from the phone. This can be done by both Android and iOS devices. However, while in background, iPhones switch the advertising to a shrinked mode that makes the packet unidentifiable. The problem of identifying an advertising iOS devices in background is still open.
On the raspberry, I used PyBluez to scan and looking for the presence of smartphones running (1) or (2). I report a code example:
import bluetooth
import bluetooth._bluetooth as bluez
import struct, socket, sys, select
def hci_enable_le_scan(sock):
hci_toggle_le_scan(sock, 0x01)
#Discover name and RSS of enabled BLE devices
class MyDiscoverer(bluetooth.DeviceDiscoverer):
def pre_inquiry(self):
self.done = False
def device_discovered(self, address, device_class, rssi, name):
discovery_logger.info("Discovered %s" % (address, ))
if name == "YOUR-DEVICE-FRIENDLY_NAME":
#Use the RSS for your detection / localization system
def inquiry_complete(self):
self.done = True
#Performs inquiry for name request
def async_inquiry():
d = MyDiscoverer()
while True:
d.find_devices(lookup_names = True)
readfiles = [ d, ]
while True:
rfds = select.select( readfiles, [], [] )[0]
if d in rfds:
d.process_event()
if d.done:
break
time.sleep(DISCOVERY_INTERVAL)
#Parse received advertising packets
def parse_events(sock):
# save current filter
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)
flt = bluez.hci_filter_new()
bluez.hci_filter_all_events(flt)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )
while True:
pkt = sock.recv(255)
ptype, event, plen = struct.unpack("BBB", pkt[:3])
if event == LE_META_EVENT:
subevent, = struct.unpack("B", pkt[3])
pkt = pkt[4:]
if subevent == EVT_LE_CONN_COMPLETE:
le_handle_connection_complete(pkt)
elif subevent == EVT_LE_ADVERTISING_REPORT:
#Check if the advertisement is the one we are searching for
if getASCII(pkt[start:end]) == "YOUR-UUID"
report_pkt_offset = 0
report_data_length, = struct.unpack("B", pkt[report_pkt_offset + 9])
# each report is 2 (event type, bdaddr type) + 6 (the address)
# + 1 (data length field) + report_data length + 1 (rssi)
report_pkt_offset = report_pkt_offset + 10 + report_data_length + 1
rssi, = struct.unpack("b", pkt[report_pkt_offset -1])
#Now you have the RSS indicator, use it for monitoring / localization
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
dev_id = 0
try:
sock = bluez.hci_open_dev(dev_id)
except:
print "error accessing bluetooth device..."
sys.exit(1)
p = threading.Thread(group=None, target=parse_events, name='parsing', args=(sock, ))
d = threading.Thread(group=None, target=async_inquiry, name='async_inquiry', args=())
try:
p.start()
except:
print "Error: unable to start parsing thread"
try:
d.start()
except:
print "Error: unable to start asynchronous discovery thread"