Arduino DLNA Server
Loading...
Searching...
No Matches
DLNAControlPointMgr.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4
6#include "DLNADevice.h"
7#include "DLNADeviceInfo.h"
8#include "Schedule.h"
9#include "Scheduler.h"
10#include "basic/StrPrint.h"
11#include "basic/Url.h"
12#include "http/HttpServer.h"
13#include "xml/XMLDeviceParser.h"
14#include "xml/XMLParser.h"
15#include "xml/XMLParserPrint.h"
16
17namespace tiny_dlna {
18
19class DLNAControlPointMgr;
21
59 public:
62
64 DLNAControlPointMgr(HttpServer& server, int port = 80)
66 setHttpServer(server, port);
67 }
69 void setParseDevice(bool flag) { is_parse_device = flag; }
70
72 void setLocalURL(Url url) { local_url = url; }
73
75 void setSearchRepeatMs(int repeatMs) { msearch_repeat_ms = repeatMs; }
76
90 const char* searchTarget = "ssdp:all", uint32_t minWaitMs = 3000,
91 uint32_t maxWaitMs = 60000) {
92 DlnaLogger.log(DlnaLogLevel::Info, "DLNADevice::begin");
93 search_target = searchTarget;
94 is_active = true;
95 p_udp = &udp;
96 p_http = &http;
97
98 if (p_http_server && http_server_port > 0 && eventCallback != nullptr) {
99 // handle server requests
101 DlnaLogger.log(DlnaLogLevel::Error, "HttpServer begin failed");
102 return false;
103 }
104 }
105
106 // setup multicast UDP
107 if (!(p_udp->begin(DLNABroadcastAddress))) {
108 DlnaLogger.log(DlnaLogLevel::Error, "UDP begin failed");
109 return false;
110 }
111
112 // Send MSearch request via UDP. Use maxWaitMs as the emission window.
113 MSearchSchedule* search =
114 new MSearchSchedule(DLNABroadcastAddress, searchTarget);
115
116 // ensure min <= max
117 if (minWaitMs > maxWaitMs) minWaitMs = maxWaitMs;
118 search->end_time = millis() + maxWaitMs;
120 search->active = true;
121 scheduler.add(search);
122
123 // if maxWaitMs > 0 we will block here and process events. We guarantee
124 // we wait at least minWaitMs before returning; we stop waiting after
125 // maxWaitMs or earlier if stopWhenFound is true AND minWaitMs has elapsed
126 uint64_t start = millis();
127 uint64_t minEnd = start + minWaitMs;
128 uint64_t maxEnd = start + maxWaitMs;
129 while (millis() < maxEnd) {
130 // if a device is found and we've satisfied the minimum wait, we can
131 // return early (we always allow early return after minWaitMs when a
132 // device has been discovered)
133 if (devices.size() > 0 && millis() >= minEnd) break;
134 loop();
135 }
136 // If we exited early because a device was found, deactivate the MSearch
137 // schedule so it will stop repeating. The scheduler will clean up
138 // inactive schedules on its next pass.
139 if (devices.size() > 0 && search != nullptr) {
140 search->active = false;
141 }
142
144 "Control Point started with %d devices found",
145 devices.size());
146 return devices.size() > 0;
147 }
148
150 void end() {
151 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::end");
152 // p_server->end();
153 for (auto& device : devices) device.clear();
154 is_active = false;
155 if (p_http_server) {
157 }
158 }
159
162 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::addAction");
163 actions.push_back(act);
164 return actions[actions.size() - 1];
165 }
166
169 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::executeActions");
170 reply.clear();
172 actions.clear();
173 // Debug: dump collected reply arguments for verification
174 DlnaLogger.log(DlnaLogLevel::Info, "Collected reply arguments: %d",
175 (int)reply.arguments.size());
176 for (auto& a : reply.arguments) {
177 DlnaLogger.log(DlnaLogLevel::Info, " -> %s = %s",
178 a.name ? a.name : "(null)", a.value.c_str());
179 }
180 return reply;
181 }
182
184 bool subscribeNotifications(DLNADeviceInfo& device, int timeoutSeconds = 60) {
186 "DLNAControlPointMgr::subscribeNotifications");
187 if (p_http_server == nullptr) {
190 "HttpServer not defined - cannot subscribe to notifications");
191 return false;
192 }
193 if (StrView(local_url.url()).isEmpty()) {
194 DlnaLogger.log(DlnaLogLevel::Error, "Local URL not defined");
195 return false;
196 }
197 for (auto& service : device.getServices()) {
198 if (StrView(service.event_sub_url).isEmpty()) {
200 "Service %s has no eventSubURL defined",
201 service.service_id);
202 continue;
203 }
204 if (!subscribeNotifications(service, timeoutSeconds)) {
205 DlnaLogger.log(DlnaLogLevel::Error, "Subscription to service %s failed",
206 service.service_id);
207 return false;
208 }
210 "Subscribed to service %s successfully",
211 service.service_id);
212 }
213 return true;
214 }
215
218 int timoutSeconds = 60) {
219 if (p_http_server == nullptr) {
222 "HttpServer not defined - cannot subscribe to notifications");
223 return false;
224 }
226 "DLNAControlPointMgr::subscribeNotifications");
227 DLNADeviceInfo& device = getDevice(service);
228
229 char url_buffer[200] = {0};
230 char seconds_txt[80] = {0};
231 Url url{getUrl(device, service.event_sub_url, url_buffer, 200)};
232 snprintf(seconds_txt, 80, "Second-%d", timoutSeconds);
233 p_http->request().put("NT", "upnp:event");
234 p_http->request().put("TIMEOUT", seconds_txt);
235 p_http->request().put("CALLBACK", local_url.url());
236 int rc = p_http->subscribe(url);
237 DlnaLogger.log(DlnaLogLevel::Info, "Http rc: %s", rc);
238 return rc == 200;
239 }
240
243 std::function<void(void* reference, const char* sid, const char* varName,
244 const char* newValue)>
245 cb) {
246 eventCallback = cb;
247 }
248
250 void setReference(void* ref) { reference = ref; }
251
253 void setHttpServer(HttpServer& server, int port = 80) {
254 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::setHttpServer");
255 p_http_server = &server;
256 http_server_port = port;
257 attachHttpServer(server);
258 }
259
262 bool loop() {
263 if (!is_active) return false;
265
266 // process UDP requests
267 RequestData req = p_udp->receive();
268 if (req && scheduler.isMSearchActive()) {
269 Schedule* schedule = parser.parse(req);
270 if (schedule != nullptr) {
271 // handle NotifyReplyCP
272 if (StrView(schedule->name()).equals("NotifyReplyCP")) {
273 NotifyReplyCP& notify_schedule = *(NotifyReplyCP*)schedule;
274 // notify_schedule.callback = processDevice;
275 processDevice(notify_schedule);
276 }
277 // handle MSearchReplyCP (HTTP/1.1 200 OK responses to M-SEARCH)
278 if (StrView(schedule->name()).equals("MSearchReplyCP")) {
279 MSearchReplyCP& ms_schedule = *(MSearchReplyCP*)schedule;
280 // Process M-SEARCH replies immediately: they contain LOCATION URLs
281 // pointing to device descriptions. Add the device and free the
282 // temporary schedule object.
283 processMSearchReply(ms_schedule);
284 }
285 }
286 }
287
288 // execute scheduled udp replys
290
291 if (p_http_server) {
292 // handle server requests
293 bool rc = p_http_server->doLoop();
294 }
295
296 // be nice, if we have other tasks
297 delay(5);
298 return true;
299 }
300
302 DLNAServiceInfo& getService(const char* id) {
303 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::getService");
304 static DLNAServiceInfo no_service(false);
305 for (auto& dev : devices) {
306 DLNAServiceInfo& result = dev.getService(id);
307 if (result) return result;
308 }
309 return no_service;
310 }
311
313 DLNADeviceInfo& getDevice(int deviceIdx = 0) {
314 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::getDevice");
315 return devices[deviceIdx];
316 }
317
320 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::getDevice");
321 for (auto& dev : devices) {
322 for (auto& srv : dev.getServices()) {
323 if (srv == srv) return dev;
324 }
325 }
326 return NO_DEVICE;
327 }
328
331 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::getDevice");
332 for (auto& dev : devices) {
333 if (dev.getDeviceURL() == location) {
335 "DLNAControlPointMgr::getDevice: Found device %s",
336 location.url());
337 return dev;
338 }
339 }
340 return NO_DEVICE;
341 }
342
344
347 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::addDevice");
348 dev.updateTimestamp();
349 for (auto& existing_device : devices) {
350 if (dev.getUDN() == existing_device.getUDN()) {
351 DlnaLogger.log(DlnaLogLevel::Debug, "Device '%s' already exists",
352 dev.getUDN());
353 return false;
354 }
355 }
356 DlnaLogger.log(DlnaLogLevel::Info, "Device '%s' has been added",
357 dev.getUDN());
358 devices.push_back(dev);
359 return true;
360 }
361
363 bool addDevice(Url url) {
364 if (StrView(url.host()).equals("127.0.0.1")){
365 DlnaLogger.log(DlnaLogLevel::Info, "Ignoring localhost device");
366 return false;
367 }
368 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::addDevice");
369 DLNADeviceInfo& device = getDevice(url);
370 if (device != NO_DEVICE) {
371 // device already exists
372 device.setActive(true);
373 return true;
374 }
375 // http get
377 DLNAHttpRequest req;
378 int rc = req.get(url, "text/xml");
379
380 if (rc != 200) {
381 DlnaLogger.log(DlnaLogLevel::Error, "Http get to '%s' failed with %d",
382 url.url(), rc);
383 return false;
384 }
385 // get xml
386 uint8_t buffer[512];
387 while (true) {
388 int len = req.read(buffer, 512);
389 if (len == 0) break;
390 xml.write(buffer, len);
391 }
392
393 // parse xml
394 DLNADeviceInfo new_device;
395 XMLDeviceParser parser;
396 parser.parse(new_device, strings, xml.c_str());
397 new_device.device_url = url;
398 // Avoid adding the same device multiple times: check UDN uniqueness
399 for (auto& existing_device : devices) {
400 if (existing_device.getUDN() == new_device.getUDN()) {
402 "Device '%s' already exists (skipping add)",
403 new_device.getUDN());
404 // Make sure the existing device is marked active and keep the URL
405 existing_device.setActive(true);
406 return true;
407 }
408 }
409
410 DlnaLogger.log(DlnaLogLevel::Info, "Device '%s' has been added",
411 new_device.getUDN());
412 devices.push_back(new_device);
413 return true;
414 }
415
417 void setActive(bool flag) { is_active = flag; }
418
420 bool isActive() { return is_active; }
421
422 protected:
425 IUDPService* p_udp = nullptr;
430 int msearch_repeat_ms = 10000;
431 bool is_active = false;
432 bool is_parse_device = false;
434 const char* search_target;
439 void* reference = nullptr;
440 std::function<void(void* reference, const char* sid, const char* varName,
441 const char* newValue)>
443
450 "DLNAControlPointMgr::attachHttpServer");
451 p_http_server = &server;
452 // register handler at the local path. If local_url is not set we use
453 // a default path "/dlna/events"
454 const char* path =
455 StrView(local_url.url()).isEmpty() ? "/dlna/events" : local_url.path();
456
457 // handler lambda: reads SID and body, forwards to parseAndDispatchEvent
458 auto notifyHandler = [](HttpServer* server, const char* requestPath,
460 // The DLNAControlPointMgr instance is passed as context[0]
461 DLNAControlPointMgr* cp = nullptr;
462 if (hl->contextCount > 0)
463 cp = static_cast<DLNAControlPointMgr*>(hl->context[0]);
464 if (!cp) {
465 server->replyNotFound();
466 return;
467 }
468
469 // read headers
470 HttpRequestHeader& req = server->requestHeader();
471 const char* sid = req.get("SID");
472
473 // read body
474 Str body = server->contentStr();
475
476 // build temporary NotifyReplyCP and forward
477 NotifyReplyCP tmp;
478 if (sid) tmp.subscription_id = sid;
479 tmp.xml = body.c_str();
480
481 cp->parseAndDispatchEvent(tmp);
482
483 server->replyOK();
484 };
485
486 void* ctx[1];
487 ctx[0] = this;
488 server.on(path, T_POST, notifyHandler, ctx, 1);
489 }
490
493 static bool handleNotifyByebye(Str& usn) {
494 // delegate to existing bye handling
495 return selfDLNAControlPoint->processBye(usn);
496 }
497
500 static bool isUdnKnown(const char* usn_c, DLNADeviceInfo*& outDev) {
501 outDev = nullptr;
502 if (!usn_c || *usn_c == '\0') return false;
503 const char* sep = strstr(usn_c, "::");
504 int udn_len = sep ? (int)(sep - usn_c) : (int)strlen(usn_c);
505 for (auto& dev : selfDLNAControlPoint->devices) {
506 const char* known_udn = dev.getUDN();
507 if (known_udn && strncmp(known_udn, usn_c, udn_len) == 0 &&
508 (int)strlen(known_udn) == udn_len) {
509 outDev = &dev;
510 return true;
511 }
512 }
513 return false;
514 }
515
516 static bool handleNotifyAlive(NotifyReplyCP& data) {
517 bool select = selfDLNAControlPoint->matches(data.usn.c_str());
518 DlnaLogger.log(DlnaLogLevel::Debug, "addDevice: %s -> %s", data.usn.c_str(),
519 select ? "added" : "filtered");
520 if (!select) return false;
521
522 DLNADeviceInfo* existing = nullptr;
523 if (isUdnKnown(data.usn.c_str(), existing)) {
525 "Device '%s' already known (skip GET)",
526 existing ? existing->getUDN() : "<unknown>");
527 if (existing) existing->setActive(true);
528 return true;
529 }
530
531 // Not known -> fetch and add device description
532 Url url{data.location.c_str()};
534 return true;
535 }
536
537 static bool processDevice(NotifyReplyCP& data) {
538 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::processDevice");
539 Str& nts = data.nts;
540 if (nts.equals("ssdp:byebye")) {
541 return handleNotifyByebye(nts);
542 }
543 if (nts.equals("ssdp:alive")) {
544 return handleNotifyAlive(data);
545 }
546 return false;
547 }
548
552 "DLNAControlPointMgr::processMSearchReply");
553 // data.location contains the device description URL
554 if (data.location.isEmpty()) return false;
555 DlnaLogger.log(DlnaLogLevel::Info, "MSearchReply -> add device: %s",
556 data.location.c_str());
557 // If the reply includes a USN we can avoid fetching multiple
558 // different LOCATION URLs for the same device (different network
559 // interfaces) by checking for the UDN part in known devices.
560 const char* usn_c = data.usn.c_str();
561 if (usn_c && *usn_c) {
562 const char* sep = strstr(usn_c, "::");
563 int udn_len = sep ? (int)(sep - usn_c) : (int)strlen(usn_c);
564 for (auto& dev : selfDLNAControlPoint->devices) {
565 const char* known_udn = dev.getUDN();
566 if (known_udn && strncmp(known_udn, usn_c, udn_len) == 0 &&
567 (int)strlen(known_udn) == udn_len) {
569 "MSearchReply: device '%s' already known (skip GET)",
570 known_udn);
571 dev.setActive(true);
572 return true;
573 }
574 }
575 }
576
577 Url url{data.location.c_str()};
579 return true;
580 }
581
585 "DLNAControlPointMgr::parseAndDispatchEvent");
586 // data.xml contains the <e:propertyset ...>...</e:propertyset> as a
587 // string
588 const char* xmlBuf = data.xml.c_str();
589 if (xmlBuf == nullptr || *xmlBuf == '\0') return;
590
591 struct CBRef {
593 NotifyReplyCP* data;
594 } ref;
595 ref.self = this;
596 ref.data = &data;
597
598 // Simple callback: whenever the parser reports a text node (non-empty
599 // `text`), treat `nodeName` as the variable name and `text` as its value.
600 auto cb = [](Str& nodeName, Vector<Str>& /*path*/, Str& text,
601 Str& /*attributes*/, int /*start*/, int /*len*/, void* vref) {
602 CBRef* r = static_cast<CBRef*>(vref);
603 if (text.length() > 0 && r->self && r->self->eventCallback) {
604 const char* sid = r->data->subscription_id.c_str();
605 // store stable copies in the control point's string registry
606 const char* namePtr = r->self->strings.add((char*)nodeName.c_str());
607 const char* valPtr = r->self->strings.add((char*)text.c_str());
608 // pass stored opaque reference as first parameter
609 r->self->eventCallback(r->self->reference, sid, namePtr, valPtr);
610 }
611 };
612
613 XMLParser parser(xmlBuf, cb);
614 parser.setReference(&ref);
615 parser.parse();
616 }
617
619 bool matches(const char* usn) {
620 if (StrView(search_target).equals("ssdp:all")) return true;
621 return StrView(usn).contains(search_target);
622 }
623
625 bool processBye(Str& usn) {
626 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::processBye");
627 for (auto& dev : devices) {
628 if (usn.startsWith(dev.getUDN())) {
629 for (auto& srv : dev.getServices()) {
630 srv.is_active = false;
631 if (usn.endsWith(srv.service_type)) {
632 if (srv.is_active) {
633 DlnaLogger.log(DlnaLogLevel::Info, "removeDevice: %s", usn);
634 srv.is_active = false;
635 }
636 }
637 }
638 }
639 }
640 return false;
641 }
642
658 size_t createXML(ActionRequest& action) {
659 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::createXML");
660 size_t result = xml.printXMLHeader();
661
662 result += xml.printNodeBegin(
663 "Envelope",
664 "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
665 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"",
666 "s");
667 result += xml.printNodeBegin("Body", nullptr, "s");
668
669 char ns[200];
670 StrView namespace_str(ns, 200);
671 namespace_str = "xmlns:u=\"%1\"";
672 bool ok = namespace_str.replace("%1", action.getServiceType());
673 DlnaLogger.log(DlnaLogLevel::Debug, "ns = '%s'", namespace_str.c_str());
674
675 // assert(ok);
676 result += xml.printNodeBegin(action.action, namespace_str.c_str(), "u");
677 for (auto arg : action.arguments) {
678 result += xml.printNode(arg.name, arg.value.c_str());
679 }
680 result += xml.printNodeEnd(action.action, "u");
681
682 result += xml.printNodeEnd("Body", "s");
683 result += xml.printNodeEnd("Envelope", "s");
684 return result;
685 }
686
688
690 reply.clear();
691 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::postAllActions");
692 for (auto& action : actions) {
693 if (action.getServiceType() != nullptr) postAction(action);
694 }
695 return reply;
696 }
697
699 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::postAction: %s",
700 action.action);
701 reply.clear();
702 DLNAServiceInfo& service = *action.p_service;
703 DLNADeviceInfo& device = getDevice(service);
704
705 // Build request body
706 StrPrint requestBody;
707 buildActionBody(action, requestBody);
708
709 // create SOAPACTION header value
710 char act[200];
711 StrView action_str{act, 200};
712 action_str = "\"";
713 action_str.add(action.getServiceType());
714 action_str.add("#");
715 action_str.add(action.action);
716 action_str.add("\"");
717
718 // crate control url
719 char url_buffer[200] = {0};
720 Url post_url{getUrl(device, service.control_url, url_buffer, 200)};
721
722 // send HTTP POST and collect response into an XMLParserPrint so we can
723 // parse incrementally
724 return processActionHttpPost(post_url, requestBody, action_str.c_str());
725 }
726
729 xml.setOutput(out);
730 createXML(action);
731 }
732
733 // Send an HTTP POST for the given URL and request body. On success the
737 const char* soapAction) {
738 XMLParserPrint xml_parser;
739 uint8_t buffer[200];
740 Str outNodeName;
741 Vector<Str> outPath;
742 Str outText;
743 Str outAttributes;
744 xml_parser.setExpandEncoded(true);
745
746 // set header and post
747 p_http->stop();
748 p_http->request().put("SOAPACTION", soapAction);
749 int rc = p_http->post(post_url, "text/xml", requestBody.c_str(),
750 requestBody.length());
751 if (rc != 200) {
752 p_http->stop();
753 reply.setValid(false);
754 return reply;
755 }
756
757 reply.setValid(true);
758 reply.arguments.clear();
759 // parse response
760 while (p_http->client()->available()) {
761 int len = p_http->client()->read(buffer, 200);
762 if (len > 0) {
763 // Serial.write(buffer, len);
764 xml_parser.write(buffer, len);
765 while (xml_parser.parse(outNodeName, outPath, outText, outAttributes)) {
766 // skip didl
767 if (!outNodeName.equals("Result")) {
768 Argument arg;
769 // persist the argument name in the control point's string registry
770 arg.name = strings.add((char*)outNodeName.c_str());
771 arg.value = outText;
772 if (!(outText.isEmpty() && outAttributes.isEmpty())) {
773 reply.addArgument(arg);
774 DlnaLogger.log(DlnaLogLevel::Info, "ActionReplay '%s': %s (%s)",
775 outNodeName.c_str(), outText.c_str(), outAttributes.c_str());
776 }
777 }
778 }
779 }
780 }
781 return reply;
782 }
783
784 const char* getUrl(DLNADeviceInfo& device, const char* suffix,
785 const char* buffer, int len) {
786 DlnaLogger.log(DlnaLogLevel::Debug, "DLNAControlPointMgr::getUrl");
787 StrView url_str{(char*)buffer, len};
788 url_str = device.getBaseURL();
789 if (url_str == nullptr) {
790 url_str.add(device.getDeviceURL().protocol());
791 url_str.add("://");
792 url_str.add(device.getDeviceURL().host());
793 url_str.add(":");
794 url_str.add(device.getDeviceURL().port());
795 }
796 url_str.add(suffix);
797 return buffer;
798 }
799};
800
801} // namespace tiny_dlna
Represents the result of invoking a DLNA service Action.
Definition: Action.h:42
void clear()
Definition: Action.h:55
void setValid(bool flag)
Definition: Action.h:48
Vector< Argument > arguments
Definition: Action.h:45
void addArgument(Argument arg)
Definition: Action.h:56
Represents a request to invoke a remote DLNA service action.
Definition: Action.h:79
Vector< Argument > arguments
Definition: Action.h:96
const char * getServiceType()
Definition: Action.h:99
DLNAServiceInfo * p_service
Definition: Action.h:94
const char * action
Definition: Action.h:95
DLNA Service: Action Argument.
Definition: Action.h:11
Str value
Definition: Action.h:19
const char * name
Definition: Action.h:18
Lightweight DLNA control point manager.
Definition: DLNAControlPointMgr.h:58
void setParseDevice(bool flag)
Requests the parsing of the device information.
Definition: DLNAControlPointMgr.h:69
bool begin(DLNAHttpRequest &http, IUDPService &udp, const char *searchTarget="ssdp:all", uint32_t minWaitMs=3000, uint32_t maxWaitMs=60000)
Start discovery by sending M-SEARCH requests and process replies.
Definition: DLNAControlPointMgr.h:89
HttpServer * p_http_server
Definition: DLNAControlPointMgr.h:437
size_t createXML(ActionRequest &action)
Definition: DLNAControlPointMgr.h:658
bool matches(const char *usn)
checks if the usn contains the search target
Definition: DLNAControlPointMgr.h:619
void * reference
Definition: DLNAControlPointMgr.h:439
void setHttpServer(HttpServer &server, int port=80)
Set HttpServer instance and register the notify handler.
Definition: DLNAControlPointMgr.h:253
static bool handleNotifyByebye(Str &usn)
Definition: DLNAControlPointMgr.h:493
bool isActive()
Checks if the scheduler is active.
Definition: DLNAControlPointMgr.h:420
bool loop()
Definition: DLNAControlPointMgr.h:262
void onNotification(std::function< void(void *reference, const char *sid, const char *varName, const char *newValue)> cb)
Register a callback that will be invoked for incoming event notification.
Definition: DLNAControlPointMgr.h:242
DLNAControlPointMgr(HttpServer &server, int port=80)
Constructor supporting Notifications.
Definition: DLNAControlPointMgr.h:64
const char * search_target
Definition: DLNAControlPointMgr.h:434
bool addDevice(Url url)
Adds the device from the device xml url if it does not already exist.
Definition: DLNAControlPointMgr.h:363
void setReference(void *ref)
Attach an opaque reference pointer (optional, for caller context)
Definition: DLNAControlPointMgr.h:250
ActionReply & getLastReply()
Definition: DLNAControlPointMgr.h:687
int http_server_port
Definition: DLNAControlPointMgr.h:438
ActionReply & postAction(ActionRequest &action)
Definition: DLNAControlPointMgr.h:698
static bool processMSearchReply(MSearchReplyCP &data)
Processes an M-SEARCH HTTP 200 reply and attempts to add the device.
Definition: DLNAControlPointMgr.h:550
static bool processDevice(NotifyReplyCP &data)
Definition: DLNAControlPointMgr.h:537
void buildActionBody(ActionRequest &action, StrPrint &out)
Build the SOAP XML request body for the action into out
Definition: DLNAControlPointMgr.h:728
static bool handleNotifyAlive(NotifyReplyCP &data)
Definition: DLNAControlPointMgr.h:516
DLNAHttpRequest * p_http
Definition: DLNAControlPointMgr.h:424
DLNADeviceInfo & getDevice(Url location)
Get a device for a Url.
Definition: DLNAControlPointMgr.h:330
void attachHttpServer(HttpServer &server)
Definition: DLNAControlPointMgr.h:448
bool subscribeNotifications(DLNADeviceInfo &device, int timeoutSeconds=60)
Subscribe to changes for all device services.
Definition: DLNAControlPointMgr.h:184
StringRegistry strings
Definition: DLNAControlPointMgr.h:435
DLNAControlPointMgr()
Default constructor w/o Notifications.
Definition: DLNAControlPointMgr.h:61
DLNADeviceInfo & getDevice(DLNAServiceInfo &service)
Provides the device for a service.
Definition: DLNAControlPointMgr.h:319
const char * getUrl(DLNADeviceInfo &device, const char *suffix, const char *buffer, int len)
Definition: DLNAControlPointMgr.h:784
DLNAServiceInfo & getService(const char *id)
Provide addess to the service information.
Definition: DLNAControlPointMgr.h:302
bool is_parse_device
Definition: DLNAControlPointMgr.h:432
ActionRequest & addAction(ActionRequest act)
Registers a method that will be called.
Definition: DLNAControlPointMgr.h:161
Scheduler scheduler
Definition: DLNAControlPointMgr.h:423
void parseAndDispatchEvent(NotifyReplyCP &data)
Parse the xml content of a NotifyReplyCP and dispatch each property.
Definition: DLNAControlPointMgr.h:583
IUDPService * p_udp
Definition: DLNAControlPointMgr.h:425
ActionReply reply
Definition: DLNAControlPointMgr.h:428
XMLPrinter xml
Definition: DLNAControlPointMgr.h:429
Url local_url
Definition: DLNAControlPointMgr.h:436
bool subscribeNotifications(DLNAServiceInfo &service, int timoutSeconds=60)
Subscribe to changes for defined device service.
Definition: DLNAControlPointMgr.h:217
std::function< void(void *reference, const char *sid, const char *varName, const char *newValue)> eventCallback
Definition: DLNAControlPointMgr.h:442
Vector< ActionRequest > actions
Definition: DLNAControlPointMgr.h:427
int msearch_repeat_ms
Definition: DLNAControlPointMgr.h:430
bool addDevice(DLNADeviceInfo dev)
Adds a new device.
Definition: DLNAControlPointMgr.h:346
void setActive(bool flag)
We can activate/deactivate the scheduler.
Definition: DLNAControlPointMgr.h:417
ActionReply & postAllActions()
Definition: DLNAControlPointMgr.h:689
bool processBye(Str &usn)
processes a bye-bye message
Definition: DLNAControlPointMgr.h:625
DLNADeviceInfo & getDevice(int deviceIdx=0)
Provides the device information by index.
Definition: DLNAControlPointMgr.h:313
static bool isUdnKnown(const char *usn_c, DLNADeviceInfo *&outDev)
Definition: DLNAControlPointMgr.h:500
bool is_active
Definition: DLNAControlPointMgr.h:431
void setLocalURL(Url url)
Defines the local url (needed for subscriptions)
Definition: DLNAControlPointMgr.h:72
ActionReply & processActionHttpPost(Url &post_url, StrPrint &requestBody, const char *soapAction)
Definition: DLNAControlPointMgr.h:736
Vector< DLNADeviceInfo > devices
Definition: DLNAControlPointMgr.h:426
DLNADeviceInfo NO_DEVICE
Definition: DLNAControlPointMgr.h:433
Vector< DLNADeviceInfo > & getDevices()
Definition: DLNAControlPointMgr.h:343
void end()
Stops the processing and releases the resources.
Definition: DLNAControlPointMgr.h:150
void setSearchRepeatMs(int repeatMs)
Sets the repeat interval for M-SEARCH requests (define before begin)
Definition: DLNAControlPointMgr.h:75
ActionReply & executeActions()
Executes all registered methods.
Definition: DLNAControlPointMgr.h:168
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: DLNADeviceInfo.h:27
Vector< DLNAServiceInfo > & getServices()
Definition: DLNADeviceInfo.h:150
Url device_url
Definition: DLNADeviceInfo.h:195
void updateTimestamp()
Update the timestamp.
Definition: DLNADeviceInfo.h:180
const char * getUDN()
Provide the udn uuid.
Definition: DLNADeviceInfo.h:56
const char * getBaseURL()
Provides the base url.
Definition: DLNADeviceInfo.h:81
Url & getDeviceURL()
This method returns base url/device.xml.
Definition: DLNADeviceInfo.h:92
void setActive(bool flag)
Definition: DLNADeviceInfo.h:185
Attributes needed for the DLNA Service Definition.
Definition: DLNAServiceInfo.h:16
const char * control_url
Definition: DLNAServiceInfo.h:36
const char * event_sub_url
Definition: DLNAServiceInfo.h:37
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:103
const char * get(const char *key)
Definition: HttpHeader.h:163
Used to register and process callbacks.
Definition: HttpRequestHandlerLine.h:19
Reading and writing of Http Requests.
Definition: HttpHeader.h:359
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:118
virtual int subscribe(Url &url)
Definition: HttpRequest.h:91
Client * client()
Definition: HttpRequest.h:130
virtual int get(Url &url, const char *acceptMime=nullptr, const char *data=nullptr, int len=-1)
Definition: HttpRequest.h:77
virtual int post(Url &url, const char *mime, const char *data, int len=-1)
Definition: HttpRequest.h:61
virtual void stop()
Definition: HttpRequest.h:56
virtual int read(uint8_t *str, int len)
Definition: HttpRequest.h:97
A Simple Header only implementation of Http Server that allows the registration of callback functions...
Definition: HttpServer.h:24
bool begin(int port, const char *ssid, const char *password)
Definition: HttpServer.h:49
bool doLoop()
Legacy method: same as copy();.
Definition: HttpServer.h:404
void on(const char *url, TinyMethodID method, web_callback_fn fn, void *ctx[]=nullptr, int ctxCount=0)
register a generic handler
Definition: HttpServer.h:89
void replyOK()
write OK reply with 200 SUCCESS
Definition: HttpServer.h:363
Str contentStr()
converts the client content to a string
Definition: HttpServer.h:450
HttpRequestHeader & requestHeader()
provides the request header
Definition: HttpServer.h:380
void end()
stops the server_ptr
Definition: HttpServer.h:76
void replyNotFound()
write 404 reply
Definition: HttpServer.h:366
Abstract Interface for UDP API.
Definition: IUDPService.h:34
virtual RequestData receive()=0
virtual bool begin(int port)=0
void log(DlnaLogLevel current_level, const char *fmt...)
Print log message.
Definition: Logger.h:40
Processing at control point to handle a MSearchReply from the device.
Definition: Schedule.h:122
Str usn
Definition: Schedule.h:126
Str location
Definition: Schedule.h:125
Send MSearch request.
Definition: Schedule.h:39
Represents a notification/notify reply scheduled for control-point processing.
Definition: Schedule.h:163
Str xml
Definition: Schedule.h:171
Str subscription_id
Definition: Schedule.h:169
Str nts
Definition: Schedule.h:166
Scheduler which processes all due Schedules (to send out UDP replies)
Definition: Scheduler.h:15
bool isMSearchActive()
Returns true if there is any active schedule with name "MSearch".
Definition: Scheduler.h:59
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
Print to a dynamic string.
Definition: StrPrint.h:12
const char * c_str()
Definition: StrPrint.h:38
size_t length()
Definition: StrPrint.h:40
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:19
virtual void add(int value)
adds a int value
Definition: StrView.h:129
virtual bool isEmpty()
checks if the string is empty
Definition: StrView.h:383
virtual const char * c_str()
provides the string value as const char*
Definition: StrView.h:376
virtual bool startsWith(const char *str)
checks if the string starts with the indicated substring
Definition: StrView.h:184
virtual bool endsWith(const char *str)
checks if the string ends with the indicated substring
Definition: StrView.h:192
virtual bool replace(const char *toReplace, const int replaced)
Replaces the first instance of toReplace with replaced.
Definition: StrView.h:395
virtual bool equals(const char *str)
checks if the string equals indicated parameter string
Definition: StrView.h:178
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: StrView.h:285
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:187
Make sure that a string is stored only once.
Definition: StringRegistry.h:9
const char * add(char *in)
adds a string
Definition: StringRegistry.h:12
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
int port()
Definition: Url.h:50
const char * host()
Definition: Url.h:45
const char * protocol()
Definition: Url.h:46
const char * url()
Definition: Url.h:43
const char * path()
Definition: Url.h:44
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(DLNADeviceInfo &result, StringRegistry &strings, const char *xmlStr)
Definition: XMLDeviceParser.h:19
Helper that implements a Print interface to accumulate XML data and then parse it using XMLParser.
Definition: XMLParserPrint.h:12
size_t write(uint8_t ch) override
Definition: XMLParserPrint.h:21
void setExpandEncoded(bool flag)
Forward expand-entities setting to the underlying XMLParser.
Definition: XMLParserPrint.h:28
bool parse(Str &outNodeName, Vector< Str > &outPath, Str &outText, Str &outAttributes)
Definition: XMLParserPrint.h:30
Lightweight streaming XML parser.
Definition: XMLParser.h:27
void setReference(void *ref)
Attach an opaque user pointer to the parser instance.
Definition: XMLParser.h:60
void parse()
Parse the previously set XML buffer and invoke the callback.
Definition: XMLParser.h:90
Definition: Allocator.h:6
@ T_POST
Definition: HttpHeader.h:39
DLNAControlPointMgr * selfDLNAControlPoint
Definition: DLNAControlPointMgr.h:20
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
bool active
Definition: Schedule.h:26
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