github.com/stampzilla/stampzilla-go@v2.0.0-rc9+incompatible/pkg/hueemulator/upnp.go (about)

     1  package hueemulator
     2  
     3  import (
     4  	"bytes"
     5  	"log"
     6  	"net"
     7  	"strings"
     8  	"text/template"
     9  
    10  	"github.com/gin-gonic/gin"
    11  )
    12  
    13  const (
    14  	upnp_multicast_address = "239.255.255.250:1900"
    15  	upnp_uri               = "/upnp/setup.xml"
    16  )
    17  
    18  //OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
    19  var responseTemplateText = `HTTP/1.1 200 OK
    20  CACHE-CONTROL: max-age=86400
    21  EXT:
    22  LOCATION: http://{{.}}
    23  SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1                                                                                                  
    24  hue-bridgeid: 001E06FFFE123456 
    25  ST: urn:schemas-upnp-org:device:basic:1
    26  USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1
    27  
    28  `
    29  
    30  //var setupTemplateText = `<?xml version="1.0"?>
    31  //<root xmlns="urn:schemas-upnp-org:device-1-0">
    32  //<specVersion>
    33  //<major>1</major>
    34  //<minor>0</minor>
    35  //</specVersion>
    36  //<URLBase>http://{{.}}/</URLBase>
    37  //<device>
    38  //<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
    39  //<friendlyName>huejack</friendlyName>
    40  //<manufacturer>Royal Philips Electronics</manufacturer>
    41  //<modelName>Philips hue bridge 2012</modelName>
    42  //<modelNumber>929000226503</modelNumber>
    43  //<UDN>uuid:f6543a06-800d-48ba-8d8f-bc2949eddc33</UDN>
    44  //</device>
    45  //</root>`
    46  
    47  var setupTemplateText = `
    48  <?xml version="1.0"?>
    49  <root xmlns="urn:schemas-upnp-org:device-1-0">
    50  <specVersion>
    51  <major>1</major>
    52  <minor>0</minor>
    53  </specVersion>
    54  <URLBase>http://{{.}}/</URLBase>
    55  <device>
    56  <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
    57  <friendlyName>Amazon-Echo-HA-Bridge (192.168.42.102)</friendlyName>
    58  <manufacturer>Royal Philips Electronics</manufacturer>
    59  <manufacturerURL>http://www.armzilla..com</manufacturerURL>
    60  <modelDescription>Hue Emulator for Amazon Echo bridge</modelDescription>
    61  <modelName>Philips hue bridge 2012</modelName>
    62  <modelNumber>929000226503</modelNumber>
    63  <modelURL>http://www.armzilla.com/amazon-echo-ha-bridge</modelURL>
    64  <serialNumber>01189998819991197253</serialNumber>
    65  <UDN>uuid:88f6698f-2c83-4393-bd03-cd54a9f8595</UDN>
    66  <serviceList>
    67  <service>
    68  <serviceType>(null)</serviceType>
    69  <serviceId>(null)</serviceId>
    70  <controlURL>(null)</controlURL>
    71  <eventSubURL>(null)</eventSubURL>
    72  <SCPDURL>(null)</SCPDURL>
    73  </service>
    74  </serviceList>
    75  <presentationURL>index.html</presentationURL>
    76  <iconList>
    77  <icon>
    78  <mimetype>image/png</mimetype>
    79  <height>48</height>
    80  <width>48</width>
    81  <depth>24</depth>
    82  <url>hue_logo_0.png</url>
    83  </icon>
    84  <icon>
    85  <mimetype>image/png</mimetype>
    86  <height>120</height>
    87  <width>120</width>
    88  <depth>24</depth>
    89  <url>hue_logo_3.png</url>
    90  </icon>
    91  </iconList>
    92  </device>
    93  </root>`
    94  
    95  type upnpData struct {
    96  	Addr string
    97  	Uri  string
    98  }
    99  
   100  var setupTemplate *template.Template
   101  
   102  func upnpTemplateInit() {
   103  	var err error
   104  	setupTemplate, err = template.New("").Parse(setupTemplateText)
   105  	if err != nil {
   106  		log.Fatalln("upnpTemplateInit:", err)
   107  	}
   108  }
   109  
   110  func upnpSetup(addr string) gin.HandlerFunc {
   111  	return func(c *gin.Context) {
   112  		//w.Header().Set("Content-Type", "application/xml")
   113  		err := setupTemplate.Execute(c.Writer, addr)
   114  		if err != nil {
   115  			log.Fatalln("[WEB] upnpSetup:", err)
   116  		}
   117  	}
   118  }
   119  
   120  func upnpResponder(hostAddr string, endpoint string) {
   121  	responseTemplate, err := template.New("").Parse(responseTemplateText)
   122  
   123  	log.Println("[UPNP] listening...")
   124  	addr, err := net.ResolveUDPAddr("udp", upnp_multicast_address)
   125  	if err != nil {
   126  		log.Fatal(err)
   127  	}
   128  	l, err := net.ListenMulticastUDP("udp", nil, addr)
   129  	l.SetReadBuffer(1024)
   130  
   131  	for {
   132  		b := make([]byte, 1024)
   133  		n, src, err := l.ReadFromUDP(b)
   134  		if err != nil {
   135  			log.Fatal("[UPNP] ReadFromUDP failed:", err)
   136  		}
   137  
   138  		if strings.Contains(string(b[:n]), "MAN: \"ssdp:discover\"") {
   139  			c, err := net.DialUDP("udp", nil, src)
   140  			if err != nil {
   141  				log.Fatal("[UPNP] DialUDP failed:", err)
   142  			}
   143  
   144  			if debug {
   145  				log.Println("[UPNP] discovery request from", src)
   146  			}
   147  
   148  			// For whatever reason I can't execute the template using c as the reader,
   149  			// you HAVE to put it in a buffer first
   150  			// possible timing issue?
   151  			// don't believe me? try it
   152  			b := &bytes.Buffer{}
   153  			err = responseTemplate.Execute(b, hostAddr+endpoint)
   154  			if err != nil {
   155  				log.Fatal("[UPNP] execute template failed:", err)
   156  			}
   157  			c.Write(b.Bytes())
   158  		}
   159  	}
   160  }