class OnVif: """In subclasses, re-define class variables """ namespace = None # this is also your link to specifications wsdl_file = None # local file sub_xaddr = None # http://ipaddr:port/onvif/sub_xaddr port = None # as defined in the wsdl file def __init__(self, ip=None, port=80, user="******", password="******", use_async=False, sub_xaddr_=None): assert (isinstance(port, int)) if sub_xaddr_ is not None: # overwrite xaddr self.xaddr = "http://%s:%i/onvif/%s" % (ip, port, sub_xaddr_) else: self.xaddr = "http://%s:%i/onvif/%s" % (ip, port, self.sub_xaddr) self.user = user self.password = password self.use_async = use_async self.service_addr = "{%s}%s" % (self.namespace, self.port) if self.use_async: self.async_transport = AsyncTransport(asyncio.get_event_loop(), cache=None) self.zeep_client = Client(wsdl=self.wsdl_file, wsse=UsernameToken(self.user, self.password, use_digest=True), settings=settings, transport=self.async_transport) else: self.zeep_client = Client(wsdl=self.wsdl_file, wsse=UsernameToken(self.user, self.password, use_digest=True), settings=settings) self.ws_client = self.zeep_client.create_service( self.service_addr, self.xaddr) self.factory = self.zeep_client.type_factory( "http://www.onvif.org/ver10/schema") async def close(self): await self.async_transport.session.close() def openSpecs(self): os.system("firefox %s" % (self.namespace)) def getVariable(self, name): return self.factory.CapabilityCategory(name)
def __init__(self, ecc_url): """A wrapper around the Zeep library's SOAP client. This exists to help prevent future problems if the Client class from zeep changes. That library is under heavy development, so it might break things in the future. The methods of this class are implemented by overriding `__getattr__` to pass along calls to the `self.service` attribute. The available methods are those defined by the SOAP protocol, and they are listed below. Methods ------- GetConfigIDs() Get a list of the config files known to the ECC server. GetState() Fetch the current state of the ECC state machine. Describe(config_xml, datasource_xml) Perform the Describe transition. Prepare(config_xml, datasource_xml) Perform the Prepare transition. Configure(config_xml, datasource_xml) Perform the Configure transition. Start() Start acquisition. Stop() Stop acquisition. Breakup() Performs the inverse of Configure. Undo() Performs the inverse of Prepare or Describe. Parameters ---------- ecc_url : str The full URL of the ECC server (i.e. "http://{address}:{port}"). """ wsdl_url = os.path.join(settings.BASE_DIR, 'attpcdaq', 'daq', 'ecc.wsdl') client = SoapClient( wsdl_url) # Loads the service definition from ecc.wsdl self.service = client.create_service( '{urn:ecc}ecc', ecc_url) # This overrides the default URL from the file # This is a list of valid operations which is used in __getattr__ below. self.operations = [ 'GetState', 'Describe', 'Prepare', 'Configure', 'Start', 'Stop', 'Undo', 'Breakup', 'GetConfigIDs' ]
class DeviceOcppS(DeviceAbstract): server_address = "" from_address = "http://localhost/ChargePointService" __logger = logging.getLogger(__name__) _client: AsyncClient = None _client_service: ServiceProxy = None __server_url = "" def __init__(self, device_id): super().__init__(device_id) self.flow_frequent_delay_seconds = 30 self.protocols = ['ocpp1.5'] self.spec_meterSerialNumber = None self.spec_meterType = None self.spec_imsi = None self.spec_iccid = None self.spec_firmwareVersion = None self.spec_chargeBoxSerialNumber = None self.spec_chargePointModel = None self.spec_chargePointVendor = None self.spec_chargePointSerialNumber = None @property def logger(self) -> logging: return self.__logger async def initialize(self) -> bool: try: logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) logging.getLogger('zeep.wsdl.wsdl').setLevel(logging.WARNING) logging.getLogger('zeep.xsd.schema').setLevel(logging.WARNING) logging.getLogger('zeep.transports').setLevel(logging.WARNING) self.__server_url = f"{self.server_address}" self.logger.info( f"Trying to connect.\nURL: {self.__server_url}\nClient supported protocols: {json.dumps(self.protocols)}" ) wsdl_file_path = f"{os.path.dirname(os.path.realpath(__file__))}/wsdl/server-201206.wsdl" self._client = Client( wsdl=wsdl_file_path, settings=Settings( raw_response=False, ), plugins=[WsAddressingExtensionPlugin(self.from_address)] ) self._client_service = self._client.create_service( '{urn://Ocpp/Cs/2012/06/}CentralSystemServiceSoap', self.__server_url ) await asyncio.sleep(1) if self.register_on_initialize: await self.action_register() await self.action_heart_beat() return True except ValueError as err: await self.handle_error(ErrorMessage(err).get(), ErrorReasons.InvalidResponse) return False except BaseException as err: await self.handle_error(ErrorMessage(err).get(), ErrorReasons.InvalidResponse) return False async def end(self): pass async def start_soap_server(self): # TODO: need to be implemented # if resp_payload is not None: # resp = f"""[{MessageTypes.Resp.value},"{req_id}",{json.dumps(resp_payload)}]""" # await self._ws.send(resp) # self.logger.debug(f"Device Read, Request, Responded:\n{resp}") # else: # self.logger.warning(f"Device Read, Request, Unknown or not supported: {req_action}") pass async def action_register(self) -> bool: action = "BootNotification" self.logger.info(f"Action {action} Start") req_payload = {} if self.spec_chargePointVendor is not None: req_payload['chargePointVendor'] = self.spec_chargePointVendor if self.spec_chargePointModel is not None: req_payload['chargePointModel'] = self.spec_chargePointModel if self.spec_chargeBoxSerialNumber is not None: req_payload['chargeBoxSerialNumber'] = self.spec_chargeBoxSerialNumber if self.spec_firmwareVersion is not None: req_payload['firmwareVersion'] = self.spec_firmwareVersion if self.spec_iccid is not None: req_payload['iccid'] = self.spec_iccid if self.spec_imsi is not None: req_payload['imsi'] = self.spec_imsi if self.spec_meterType is not None: req_payload['meterType'] = self.spec_meterType if self.spec_meterSerialNumber is not None: req_payload['meterSerialNumber'] = self.spec_meterSerialNumber if self.spec_chargePointSerialNumber is not None: req_payload['chargePointSerialNumber'] = self.spec_chargePointSerialNumber resp_payload = await self.by_device_req_send(action, req_payload) if resp_payload is None or resp_payload['status'] != 'Accepted': await self.handle_error(f"Action {action} Response Failed", ErrorReasons.InvalidResponse) return False self.logger.info(f"Action {action} End") return True async def action_heart_beat(self) -> bool: action = "Heartbeat" self.logger.info(f"Action {action} Start") if await self.by_device_req_send(action, {}) is None: return False self.logger.info(f"Action {action} End") return True async def action_status_update(self, status, **options) -> bool: return await self.action_status_update_ocpp(status, "NoError", **options) async def action_status_update_ocpp(self, status, errorCode, **options) -> bool: action = "StatusNotification" self.logger.info(f"Action {action} Start") req_payload = { "connectorId": options.pop("connectorId", 1), "errorCode": errorCode, "status": status } try: await self.by_device_req_send(action, req_payload) except: return False self.logger.info(f"Action {action} End") return True async def action_authorize(self, **options) -> bool: action = "Authorize" self.logger.info(f"Action {action} Start") req_payload = { "idTag": options.pop("idTag", "-") } resp_payload = await self.by_device_req_send(action, req_payload) if resp_payload is None or resp_payload['status'] != 'Accepted': await self.handle_error(f"Action {action} Response Failed", ErrorReasons.InvalidResponse) return False self.logger.info(f"Action {action} End") return True async def action_data_transfer(self, **options) -> bool: action = "DataTransfer" self.logger.info(f"Action {action} Start") req_payload = options resp_payload = await self.by_device_req_send(action, req_payload) if resp_payload is None: return False self.logger.info(f"Action {action} End") return True charge_start_time = datetime.datetime.utcnow() charge_meter_start = 1000 async def action_charge_start(self, **options) -> bool: action = "StartTransaction" self.logger.info(f"Action {action} Start") self.charge_start_time = datetime.datetime.utcnow() self.charge_meter_start = options.pop("meterStart", self.charge_meter_start) req_payload = { "timestamp": self.utcnow_iso(), "connectorId": options.pop("connectorId", 1), "meterStart": self.charge_meter_start, "idTag": options.pop("idTag", "-") } resp_payload = await self.by_device_req_send(action, req_payload) if resp_payload is None or resp_payload['idTagInfo']['status'] != 'Accepted': await self.handle_error(f"Action {action} Response Failed", ErrorReasons.InvalidResponse) return False self.charge_id = resp_payload['transactionId'] self.charge_in_progress = True self.logger.info(f"Action {action} End") return True def charge_meter_value_current(self, **options): return math.floor(self.charge_meter_start + ( (datetime.datetime.utcnow() - self.charge_start_time).total_seconds() / 60 * options.pop("chargedKwhPerMinute", 1) * 1000 )) async def action_meter_value(self, **options) -> bool: action = "MeterValues" self.logger.info(f"Action {action} Start") req_payload = { "connectorId": options.pop("connectorId", 1), "transactionId": self.charge_id, "values": [{ "timestamp": self.utcnow_iso(), "value": [self.charge_meter_value_current(**options), { "context": "Sample.Periodic", "measurand": "Energy.Active.Import.Register", "location": "Outlet", "unit": "kWh" }] }] } try: await self.by_device_req_send(action, req_payload) except BaseException as err: await self.handle_error(ErrorMessage(err).get(), ErrorReasons.InvalidResponse) return False self.logger.info(f"Action {action} End") return True async def action_charge_stop(self, **options) -> bool: action = "StopTransaction" self.logger.info(f"Action {action} Start") req_payload = { "timestamp": self.utcnow_iso(), "transactionId": self.charge_id, "meterStop": self.charge_meter_value_current(**options), "idTag": options.pop("idTag", "-"), } resp_payload = await self.by_device_req_send(action, req_payload) if resp_payload is None or resp_payload['status'] != 'Accepted': await self.handle_error(f"Action {action} Response Failed", ErrorReasons.InvalidResponse) return False self.logger.info(f"Action {action} End") return True async def flow_heartbeat(self) -> bool: log_title = self.flow_heartbeat.__name__ self.logger.info(f"Flow {log_title} Start") if not await self.action_heart_beat(): return False self.logger.info(f"Flow {log_title} End") return True async def flow_authorize(self, **options) -> bool: log_title = self.flow_authorize.__name__ self.logger.info(f"Flow {log_title} Start") if not await self.action_authorize(**options): return False self.logger.info(f"Flow {log_title} End") return True async def flow_charge(self, auto_stop: bool, **options) -> bool: log_title = self.flow_charge.__name__ self.logger.info(f"Flow {log_title} Start") if not await self.action_authorize(**options): self.charge_in_progress = False return False if not await self.action_charge_start(**options): self.charge_in_progress = False return False if not await self.action_status_update("Preparing", **options): self.charge_in_progress = False return False if not await self.action_status_update("Charging", **options): self.charge_in_progress = False return False if not await self.flow_charge_ongoing_loop(auto_stop, **options): self.charge_in_progress = False return False if not await self.action_status_update("Finishing", **options): self.charge_in_progress = False return False if not await self.action_charge_stop(**options): self.charge_in_progress = False return False if not await self.action_status_update("Available", **options): self.charge_in_progress = False return False self.logger.info(f"Flow {log_title} End") self.charge_in_progress = False return True async def flow_charge_ongoing_actions(self, **options) -> bool: return await self.action_meter_value(**options) async def by_device_req_send(self, action, req_payload) -> typing.Any: req_id = str(uuid.uuid4()) req = req_payload return await self.by_device_req_send_raw(req, action, req_id) async def by_device_req_send_raw(self, raw, action, req_id=None) -> typing.Any: if req_id is None: req_id = str(uuid.uuid4()) self.logger.debug(f"By Device Req ({action}):\n{raw}") try: result = self._client_service[action](**raw, _soapheaders={ 'ChargeBoxIdentity': self.deviceId, }) self.logger.debug(f"By Device Resp ({action}):\n{result}") return result except asyncio.TimeoutError: return self.by_device_req_resp_timeout() async def by_middleware_req(self, req_id: str, req_action: str, req_payload: typing.Any): resp_payload = None if req_action in map(lambda x: str(x).lower(), [ "ClearCache", "ChangeAvailability", "RemoteStartTransaction", "RemoteStopTransaction", "SetChargingProfile", "ChangeConfiguration", "UnlockConnector", "UpdateFirmware", "SendLocalList", "CancelReservation", "ReserveNow", "Reset", "DataTransfer", ]): resp_payload = { "status": "Accepted" } elif req_action == "GetConfiguration".lower(): resp_payload = { "configurationKey": [ {"key": "type", "value": "device-simulator", "readonly": "true"}, {"key": "server_address", "value": self.server_address, "readonly": "true"}, {"key": "identifier", "value": self.deviceId, "readonly": "false"}, ] } elif req_action == "GetDiagnostics".lower(): resp_payload = { "fileName": "fake_file_name.log" } if req_action == "RemoteStartTransaction".lower(): if not self.charge_can_start(): resp_payload["status"] = "Rejected" else: options = { "connectorId": req_payload["connectorId"] if "connectorId" in req_payload else 0, "idTag": req_payload["idTag"] if "idTag" in req_payload else "-", } self.logger.info(f"Device, Read, Request, RemoteStart, Options: {json.dumps(options)}") asyncio.create_task(utility.run_with_delay(self.flow_charge(False, **options), 2)) if req_action == "RemoteStopTransaction".lower(): if not self.charge_can_stop(req_payload["transactionId"] if "transactionId" in req_payload else 0): resp_payload["status"] = "Rejected" else: asyncio.create_task(utility.run_with_delay(self.flow_charge_stop(), 2)) if req_action == "Reset".lower(): asyncio.create_task(utility.run_with_delay(self.re_initialize(), 2)) return resp_payload async def flow_charge_stop(self): self.charge_in_progress = False pass async def loop_interactive_custom(self): is_back = False while not is_back: input1 = await aioconsole.ainput(""" What should I do? (enter the number + enter) 0: Back 1: HeartBeat 2: StatusUpdate 99: Full custom """) if input1 == "0": is_back = True elif input1 == "1": await self.action_heart_beat() elif input1 == "2": input1 = await aioconsole.ainput("Which status?\n") input2 = await aioconsole.ainput("Which errorCode?\n") input3 = await aioconsole.ainput("Which connector?\n") await self.action_status_update_ocpp(input1, input2, ** { 'connectorId': input3, }) elif input1 == "99": input_action = await aioconsole.ainput("Enter full custom action name:\n") input_payload = await aioconsole.ainput("Enter full custom payload:\n") await self.by_device_req_send_raw(json.loads(input_payload), input_action) pass
class ONVIFService: """ Python Implemention for ONVIF Service. Services List: DeviceMgmt DeviceIO Event AnalyticsDevice Display Imaging Media PTZ Receiver RemoteDiscovery Recording Replay Search Extension >>> from onvif import ONVIFService >>> device_service = ONVIFService('http://192.168.0.112/onvif/device_service', ... 'admin', 'foscam', ... '/etc/onvif/wsdl/devicemgmt.wsdl') >>> ret = device_service.GetHostname() >>> print ret.FromDHCP >>> print ret.Name >>> device_service.SetHostname(dict(Name='newhostname')) >>> ret = device_service.GetSystemDateAndTime() >>> print ret.DaylightSavings >>> print ret.TimeZone >>> dict_ret = device_service.to_dict(ret) >>> print dict_ret['TimeZone'] There are two ways to pass parameter to services methods 1. Dict params = {'Name': 'NewHostName'} device_service.SetHostname(params) 2. Type Instance params = device_service.create_type('SetHostname') params.Hostname = 'NewHostName' device_service.SetHostname(params) """ @safeFunc def __init__(self, xaddr, wsse: UsernameDigestTokenDtDiff, url: Path, *, bindingName='', transport=None): if not url.is_file(): raise ONVIFError('%s doesn`t exist!' % url) self.url = url self.xaddr = xaddr if not transport: transport = AsyncTransport(None) self.client = Client(wsdl=str(url), wsse=wsse, transport=transport, settings=Settings(strict=False, xml_huge_tree=True)) self.wsClient = self.client.create_service(bindingName, xaddr) self.bindingName = bindingName def createType(self, name): """ create type """ bindingName = self.bindingName namespace = bindingName[bindingName.find('{') + 1:bindingName.find('}')] client = self.client availableNs = client.namespaces ns = list(availableNs.keys())[list( availableNs.values()).index(namespace)] return client.get_element((ns or 'ns0') + ':' + name)() @staticmethod @safeFunc def to_dict(zeepobject): # Convert a WSDL Type instance into a dictionary return {} if zeepobject is None else zeep.helpers.serialize_object( zeepobject) @classmethod def service_wrapper(cls, func): @safeFunc def wrapped(params=None): params = {} if params is None else cls.to_dict(params) try: ret = func(**params) except TypeError: ret = func(params) return ret return wrapped def __getattr__(self, name): """ Call the real onvif Service operations, See the official wsdl definition for the APIs detail(API name, request parameters, response parameters, parameter types, etc...) """ builtin = name.startswith('__') and name.endswith('__') if builtin: return self.__dict__[name] else: return self.service_wrapper(getattr(self.wsClient, name))