Arduino DLNA Server
DLNAControlPointMgr.h
Go to the documentation of this file.
1 #pragma once
2 
4 #include "DLNADeviceMgr.h"
5 #include "DLNADevice.h"
6 #include "Schedule.h"
7 #include "Scheduler.h"
8 #include "basic/StrPrint.h"
9 #include "basic/Url.h"
10 #include "http/HttpServer.h"
11 #include "xml/XMLDeviceParser.h"
12 
13 namespace tiny_dlna {
14 
15 class DLNAControlPointMgr;
17 
33  public:
36  void setParseDevice(bool flag) { is_parse_device = flag; }
37 
50  bool begin(HttpRequest& http, IUDPService& udp,
51  const char* searchTarget = "ssdp:all", uint32_t processingTime = 0,
52  bool stopWhenFound = true) {
53  DlnaLogger.log(DlnaInfo, "DLNADevice::begin");
54  search_target = searchTarget;
55  is_active = true;
56  p_udp = &udp;
57  p_http = &http;
58 
59  // setup UDP
60  if (!p_udp->begin(DLNABroadcastAddress)) {
61  DlnaLogger.log(DlnaError, "UDP begin failed");
62  return false;
63  }
64 
65  // Send MSearch request via UDP
66  MSearchSchedule* search =
67  new MSearchSchedule(DLNABroadcastAddress, searchTarget);
68  search->end_time = millis() + 3000;
69  search->repeat_ms = 1000;
70  scheduler.add(search);
71 
72  // if processingTime > 0 we do some loop processing already here
73  uint64_t end = millis() + processingTime;
74  while (millis() < end) {
75  if (stopWhenFound && devices.size() > 0) break;
76  loop();
77  }
78 
79  DlnaLogger.log(DlnaInfo, "Control Point started with %d devices found",
80  devices.size());
81  return devices.size() > 0;
82  }
83 
85  void end() {
86  // p_server->end();
87  for (auto& device : devices) device.clear();
88  is_active = false;
89  }
90 
93  actions.push_back(act);
94  return actions[actions.size() - 1];
95  }
96 
99  ActionReply result = postAllActions();
100  actions.clear();
101  return result;
102  }
103 
104  // /// Provides the actual state value
105  // const char* getStateValue(const char* name) {
106  // for (auto& st : state) {
107  // if (st.name == name) {
108  // return st.value.c_str();
109  // }
110  // }
111  // return nullptr;
112  // }
113 
116  bool loop() {
117  if (!is_active) return false;
119 
120  // process UDP requests
121  RequestData req = p_udp->receive();
122  if (req) {
123  Schedule* schedule = parser.parse(req);
124  if (schedule != nullptr) {
125  // handle NotifyReplyCP
126  if (StrView(schedule->name()).equals("NotifyReplyCP")) {
127  NotifyReplyCP& notify_schedule = *(NotifyReplyCP*)schedule;
128  notify_schedule.callback = processDevice;
129  }
130  scheduler.add(schedule);
131  }
132  }
133 
134  // execute scheduled udp replys
136 
137  // be nice, if we have other tasks
138  delay(5);
139  return true;
140  }
141 
143  DLNAServiceInfo& getService(const char* id) {
144  static DLNAServiceInfo no_service(false);
145  for (auto& dev : devices) {
146  DLNAServiceInfo& result = dev.getService(id);
147  if (result) return result;
148  }
149  return no_service;
150  }
151 
153  DLNADevice& getDevice(int deviceIdx = 0) { return devices[deviceIdx]; }
154 
157  for (auto& dev : devices) {
158  for (auto& srv : dev.getServices()) {
159  if (srv == srv) return dev;
160  }
161  }
162  return NO_DEVICE;
163  }
164 
166  DLNADevice& getDevice(Url location) {
167  for (auto& dev : devices) {
168  if (dev.getDeviceURL() == location) {
169  return dev;
170  }
171  }
172  return NO_DEVICE;
173  }
174 
176  return devices;
177  }
178 
180  bool addDevice(DLNADevice dev) {
181  dev.updateTimestamp();
182  for (auto& existing_device : devices) {
183  if (dev.getUDN() == existing_device.getUDN()) {
184  DlnaLogger.log(DlnaInfo, "Device '%s' already exists", dev.getUDN());
185  return false;
186  }
187  }
188  DlnaLogger.log(DlnaInfo, "Device '%s' has been added", dev.getUDN());
189  devices.push_back(dev);
190  return true;
191  }
192 
194  bool addDevice(Url url) {
195  DLNADevice& device = getDevice(url);
196  if (device != NO_DEVICE){
197  // device already exists
198  device.setActive(true);
199  return true;
200  }
201  // http get
202  StrPrint xml;
203  HttpRequest req;
204  int rc = req.get(url, "text/xml");
205 
206  if (rc != 200) {
207  DlnaLogger.log(DlnaError, "Http get to '%s' failed with %d", url.url(), rc);
208  req.stop();
209  return false;
210  }
211  // get xml
212  uint8_t buffer[512];
213  while(true){
214  int len = req.read(buffer, 512);
215  if (len == 0) break;
216  xml.write(buffer, len);
217  }
218  req.stop();
219 
220  // parse xml
221  DLNADevice new_device;
222  XMLDeviceParser parser;
223  parser.parse(new_device, xml.c_str());
224  devices.push_back(new_device);
225  new_device.device_url = url;
226  return true;
227  }
228 
230  void setActive(bool flag) { is_active = flag; }
231 
233  bool isActive() { return is_active; }
234 
235  protected:
237  IUDPService* p_udp = nullptr;
238  HttpRequest* p_http = nullptr;
240  // Vector<StateValue> state;
243  bool is_active = false;
244  bool is_parse_device = false;
246  const char* search_target;
247 
249  static bool processDevice(NotifyReplyCP& data) {
250  Str& nts = data.nts;
251  if (nts.equals("ssdp:byebye")) {
253  return true;
254  }
255  if (nts.equals("ssdp:alive")) {
256  bool select = selfDLNAControlPoint->matches(data.usn.c_str());
257  DlnaLogger.log(DlnaInfo, "addDevice: %s -> %s", data.usn.c_str(),
258  select ? "added" : "filtered");
259  Url url{data.location.c_str()};
261  return true;
262  }
263  return false;
264  }
265 
267  bool matches(const char* usn) {
268  if (StrView(search_target).equals("ssdp:all")) return true;
269  return StrView(usn).contains(search_target);
270  }
271 
273  bool processBye(Str& usn) {
274  for (auto& dev : devices) {
275  if (usn.startsWith(dev.getUDN())) {
276  for (auto& srv : dev.getServices()) {
277  srv.is_active = false;
278  if (usn.endsWith(srv.service_type)) {
279  if (srv.is_active) {
280  DlnaLogger.log(DlnaInfo, "removeDevice: %s", usn);
281  srv.is_active = false;
282  }
283  }
284  }
285  }
286  }
287  return false;
288  }
289 
305  size_t createXML(ActionRequest& action) {
306  size_t result = xml.printXMLHeader();
307 
308  result += xml.printNodeBegin(
309  "Envelope",
310  "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
311  "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"",
312  "s");
313  result += xml.printNodeBegin("Body", nullptr, "s");
314 
315  char ns[200];
316  StrView namespace_str(ns, 200);
317  namespace_str = "xmlns:u=\"%1\"";
318  bool ok = namespace_str.replace("%1", action.getServiceType());
319  DlnaLogger.log(DlnaInfo, "ns = '%s'", namespace_str.c_str());
320 
321  //assert(ok);
322  result += xml.printNodeBegin(action.action, namespace_str.c_str(), "u");
323  for (auto arg : action.arguments) {
324  result += xml.printNode(arg.name, arg.value.c_str());
325  }
326  result += xml.printNodeEnd(action.action, "u");
327 
328  result += xml.printNodeEnd("Body", "s");
329  result += xml.printNodeEnd("Envelope", "s");
330  return result;
331  }
332 
334  ActionReply result;
335  for (auto& action : actions) {
336  if (action.getServiceType()!=nullptr)
337  result.add(postAction(action));
338  }
339  return result;
340  }
341 
343  DLNAServiceInfo& service = *action.p_service;
344  DLNADevice& device = getDevice(service);
345 
346  // create XML
347  StrPrint str_print;
348  xml.setOutput(str_print);
349  createXML(action);
350  // log xml request
351  DlnaLogger.log(DlnaInfo, str_print.c_str());
352 
353  // create SOAPACTION header
354  char act[200];
355  StrView action_str{act, 200};
356  action_str = "\"";
357  action_str.add(action.getServiceType());
358  action_str.add("#");
359  action_str.add(action.action);
360  action_str.add("\"");
361 
362  p_http->request().put("SOAPACTION", action_str.c_str());
363 
364  // crate control url
365  char url_buffer[200];
366  StrView url_str{url_buffer, 200};
367  url_str = device.getBaseURL().url();
368  url_str.add(service.control_url);
369  Url post_url{url_str.c_str()};
370 
371  // post the request
372  int rc = p_http->post(post_url, "text/xml", str_print.c_str(),
373  str_print.length());
374  DlnaLogger.log(DlnaInfo, "==> %d", rc);
375 
376  // receive result
377  str_print.reset();
378  uint8_t buffer[200];
379  while (p_http->client()->available()) {
380  int len = p_http->client()->read(buffer, 200);
381  str_print.write(buffer, len);
382  }
383 
384  // log result
385  DlnaLogger.log(DlnaInfo, str_print.c_str());
386  p_http->stop();
387 
388  ActionReply result(rc == 200);
389  return result;
390  }
391 };
392 
393 } // namespace tiny_dlna
Definition: Action.h:38
void add(ActionReply alt)
Definition: Action.h:44
Definition: Action.h:55
Vector< Argument > arguments
Definition: Action.h:72
const char * getServiceType()
Definition: Action.h:75
DLNAServiceInfo * p_service
Definition: Action.h:70
const char * action
Definition: Action.h:71
Setup of a Basic DLNA Control Point. The control point.
Definition: DLNAControlPointMgr.h:32
void setParseDevice(bool flag)
Requests the parsing of the device information.
Definition: DLNAControlPointMgr.h:36
DLNADevice & getDevice(int deviceIdx=0)
Provides the device information by index.
Definition: DLNAControlPointMgr.h:153
size_t createXML(ActionRequest &action)
Definition: DLNAControlPointMgr.h:305
bool matches(const char *usn)
checks if the usn contains the search target
Definition: DLNAControlPointMgr.h:267
bool isActive()
Checks if the scheduler is active.
Definition: DLNAControlPointMgr.h:233
DLNADevice & getDevice(Url location)
Get a device for a Url.
Definition: DLNAControlPointMgr.h:166
DLNADevice & getDevice(DLNAServiceInfo &service)
Provides the device for a service.
Definition: DLNAControlPointMgr.h:156
bool loop()
Definition: DLNAControlPointMgr.h:116
const char * search_target
Definition: DLNAControlPointMgr.h:246
bool addDevice(Url url)
Adds the device from the device xml url if it does not already exist.
Definition: DLNAControlPointMgr.h:194
DLNADevice NO_DEVICE
Definition: DLNAControlPointMgr.h:245
ActionReply postAllActions()
Definition: DLNAControlPointMgr.h:333
ActionRequest & addAction(ActionRequest act)
Registers a method that will be called.
Definition: DLNAControlPointMgr.h:92
bool addDevice(DLNADevice dev)
Adds a new device.
Definition: DLNAControlPointMgr.h:180
Vector< DLNADevice > devices
Definition: DLNAControlPointMgr.h:239
ActionReply postAction(ActionRequest &action)
Definition: DLNAControlPointMgr.h:342
static bool processDevice(NotifyReplyCP &data)
Processes a NotifyReplyCP message.
Definition: DLNAControlPointMgr.h:249
DLNAControlPointMgr()
Definition: DLNAControlPointMgr.h:34
bool is_parse_device
Definition: DLNAControlPointMgr.h:244
Scheduler scheduler
Definition: DLNAControlPointMgr.h:236
IUDPService * p_udp
Definition: DLNAControlPointMgr.h:237
XMLPrinter xml
Definition: DLNAControlPointMgr.h:242
HttpRequest * p_http
Definition: DLNAControlPointMgr.h:238
Vector< ActionRequest > actions
Definition: DLNAControlPointMgr.h:241
void setActive(bool flag)
We can activate/deactivate the scheduler.
Definition: DLNAControlPointMgr.h:230
bool processBye(Str &usn)
processes a bye-bye message
Definition: DLNAControlPointMgr.h:273
bool begin(HttpRequest &http, IUDPService &udp, const char *searchTarget="ssdp:all", uint32_t processingTime=0, bool stopWhenFound=true)
Definition: DLNAControlPointMgr.h:50
Vector< DLNADevice > & getDevices()
Definition: DLNAControlPointMgr.h:175
bool is_active
Definition: DLNAControlPointMgr.h:243
void end()
Stops the processing and releases the resources.
Definition: DLNAControlPointMgr.h:85
ActionReply executeActions()
Executes all registered methods.
Definition: DLNAControlPointMgr.h:98
DLNAServiceInfo & getService(const char *id)
Provide addess to the service information.
Definition: DLNAControlPointMgr.h:143
Translates DLNA UDP Requests to Schedule so that we can schedule a reply.
Definition: DLNAControlPointRequestParser.h:15
Schedule * parse(RequestData &req)
Definition: DLNAControlPointRequestParser.h:17
Device Attributes and generation of XML using urn:schemas-upnp-org:device-1-0. We could just return a...
Definition: DLNADevice.h:27
void setActive(bool flag)
Definition: DLNADevice.h:159
Url device_url
Definition: DLNADevice.h:168
Url & getBaseURL()
Provides the base url.
Definition: DLNADevice.h:56
const char * getUDN()
Provide the udn uuid.
Definition: DLNADevice.h:50
void updateTimestamp()
Update the timestamp.
Definition: DLNADevice.h:154
Attributes needed for the DLNA Service Definition.
Definition: DLNAServiceInfo.h:16
const char * control_url
Definition: DLNAServiceInfo.h:36
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:102
Simple API to process get, put, post, del http requests I tried to use Arduino HttpClient,...
Definition: HttpRequest.h:21
virtual HttpRequestHeader & request()
Definition: HttpRequest.h:111
virtual int get(Url &url, const char *acceptMime=nullptr, const char *data=nullptr, int len=-1)
Definition: HttpRequest.h:75
Client * client()
Definition: HttpRequest.h:123
virtual int post(Url &url, const char *mime, const char *data, int len=-1)
Definition: HttpRequest.h:59
virtual void stop()
Definition: HttpRequest.h:54
virtual int read(uint8_t *str, int len)
Definition: HttpRequest.h:90
Abstract Interface for UDP API.
Definition: IUDPService.h:34
virtual RequestData receive()=0
virtual bool begin(IPAddressAndPort addr)=0
void log(DlnaLogLevel current_level, const char *fmt...)
Print log message.
Definition: Logger.h:40
Str usn
Definition: Schedule.h:125
Str location
Definition: Schedule.h:124
Send MSearch request.
Definition: Schedule.h:39
Definition: Schedule.h:135
std::function< bool(NotifyReplyCP &ref)> callback
Definition: Schedule.h:146
Str nts
Definition: Schedule.h:138
Scheduler which processes all due Schedules (to send out UDP replies)
Definition: Scheduler.h:15
void execute(IUDPService &udp)
Execute all due schedules.
Definition: Scheduler.h:25
void add(Schedule *schedule)
Add a schedule to the scheduler.
Definition: Scheduler.h:18
Definition: StrPrint.h:11
size_t length()
Definition: StrPrint.h:32
size_t write(uint8_t ch) override
Definition: StrPrint.h:14
void reset()
Definition: StrPrint.h:34
const char * c_str()
Definition: StrPrint.h:30
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:25
virtual void add(int value)
adds a int value
Definition: StrView.h:130
virtual bool startsWith(const char *str)
checks if the string starts with the indicated substring
Definition: StrView.h:177
virtual const char * c_str()
provides the string value as const char*
Definition: StrView.h:366
virtual bool endsWith(const char *str)
checks if the string ends with the indicated substring
Definition: StrView.h:185
virtual bool replace(const char *toReplace, const int replaced)
Replaces the first instance of toReplace with replaced.
Definition: StrView.h:385
virtual bool equals(const char *str)
checks if the string equals indicated parameter string
Definition: StrView.h:171
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:277
String implementation which keeps the data on the heap. We grow the allocated memory only if the copy...
Definition: Str.h:22
const char * c_str()
provides the string value as const char*
Definition: Str.h:188
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
const char * url()
Definition: Url.h:46
Vector implementation which provides the most important methods as defined by std::vector....
Definition: Vector.h:21
Parses an DLNA device xml string to fill the DLNADevice data structure.
Definition: XMLDeviceParser.h:17
void parse(DLNADevice &result, const char *xmlStr)
Definition: XMLDeviceParser.h:19
Definition: Allocator.h:6
@ DlnaInfo
Definition: Logger.h:16
@ DlnaError
Definition: Logger.h:16
DLNAControlPointMgr * selfDLNAControlPoint
Definition: DLNAControlPointMgr.h:16
LoggerClass DlnaLogger
Definition: Logger.cpp:5
Provides information of the received UDP which consists of the (xml) data and the peer address and po...
Definition: IUDPService.h:23
An individual Schedule (to send out UDP messages)
Definition: Schedule.h:17
uint64_t end_time
Definition: Schedule.h:24
virtual const char * name()
Definition: Schedule.h:30
uint32_t repeat_ms
Definition: Schedule.h:22
Functions to efficiently output XML. XML data contains a lot of redundancy so it is more memory effic...
Definition: XMLPrinter.h:31
size_t printNodeBegin(const char *node, const char *attributes=nullptr, const char *ns=nullptr)
Definition: XMLPrinter.h:97
size_t printNode(XMLNode node)
Definition: XMLPrinter.h:43
size_t printNodeEnd(const char *node, const char *ns=nullptr)
Definition: XMLPrinter.h:122
void setOutput(Print &output)
Defines the output.
Definition: XMLPrinter.h:36
size_t printXMLHeader()
Definition: XMLPrinter.h:38