domoticz: fix Onkyo custom command handling from dzVents 6091/head
authorDavid Woodhouse <dwmw2@infradead.org>
Sun, 13 May 2018 14:54:05 +0000 (15:54 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Mon, 4 Jun 2018 10:11:25 +0000 (11:11 +0100)
https://github.com/domoticz/domoticz/pull/2386

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
utils/domoticz/patches/101_onkyo_pr2386.patch [new file with mode: 0644]

diff --git a/utils/domoticz/patches/101_onkyo_pr2386.patch b/utils/domoticz/patches/101_onkyo_pr2386.patch
new file mode 100644 (file)
index 0000000..ce62442
--- /dev/null
@@ -0,0 +1,250 @@
+From 86a4fe7e28952f969609cfb52afae06b40aa273e Mon Sep 17 00:00:00 2001
+From: David Woodhouse <dwmw2@infradead.org>
+Date: Sat, 12 May 2018 15:11:29 +0100
+Subject: [PATCH] Add generic CustomCommand handling for scripts
+
+The Onkyo-specific support in JSON and dzVents isn't really sufficient,
+because it (quite rightly) needs authentication. But dzVents just assumes
+it can make unauthenticated calls to localhost:8080, and doesn't work.
+
+I've been working around this by spawning an external script to invoke
+curl, but *that* only works over HTTPS not HTTP for some reason (without
+HTTPS I just get an 'Unauthorized' response). And that takes literally
+*seconds* to work, on the slow machine I'm using.
+
+Fix it up so that the script command arrays can have a 'CustomCommand',
+done generically so that it can easily cover devices other than Onkyo.
+---
+ .../runtime/device-adapters/onkyo_device.lua  |  4 +-
+ hardware/DomoticzHardware.cpp                 |  5 ++
+ hardware/DomoticzHardware.h                   |  1 +
+ hardware/OnkyoAVTCP.cpp                       |  4 ++
+ hardware/OnkyoAVTCP.h                         |  1 +
+ main/EventSystem.cpp                          | 57 +++++++++++++++++++
+ main/EventSystem.h                            |  1 +
+ main/SQLHelper.cpp                            |  4 ++
+ main/SQLHelper.h                              | 15 ++++-
+ 9 files changed, 88 insertions(+), 4 deletions(-)
+
+diff --git a/dzVents/runtime/device-adapters/onkyo_device.lua b/dzVents/runtime/device-adapters/onkyo_device.lua
+index ec74841f..8222fe6b 100644
+--- a/dzVents/runtime/device-adapters/onkyo_device.lua
++++ b/dzVents/runtime/device-adapters/onkyo_device.lua
+@@ -17,9 +17,7 @@ return {
+     process = function (device, data, domoticz, utils, adapterManager)
+         function device.onkyoEISCPCommand(cmd)
+-            local url = domoticz.settings['Domoticz url'] ..
+-                    '/json.htm?param=onkyoeiscpcommand&type=command&idx=' .. device.id .. '&action=' .. tostring (cmd)
+-            return domoticz.openURL(url)
++            return TimedCommand(domoticz, 'CustomCommand:' .. device.id, tostring(cmd), 'device')
+         end
+     end
+ }
+diff --git a/hardware/DomoticzHardware.cpp b/hardware/DomoticzHardware.cpp
+index 7aad167b..75db53c0 100644
+--- a/hardware/DomoticzHardware.cpp
++++ b/hardware/DomoticzHardware.cpp
+@@ -36,6 +36,11 @@ CDomoticzHardwareBase::~CDomoticzHardwareBase()
+ {
+ }
++bool CDomoticzHardwareBase::CustomCommand(const uint64_t idx, const std::string &sCommand)
++{
++      return false;
++}
++
+ bool CDomoticzHardwareBase::Start()
+ {
+       m_iHBCounter = 0;
+diff --git a/hardware/DomoticzHardware.h b/hardware/DomoticzHardware.h
+index a8790f91..660fc2de 100644
+--- a/hardware/DomoticzHardware.h
++++ b/hardware/DomoticzHardware.h
+@@ -16,6 +16,7 @@ public:
+       bool Start();
+       bool Stop();
+       virtual bool WriteToHardware(const char *pdata, const unsigned char length)=0;
++      virtual bool CustomCommand(const uint64_t idx, const std::string &sCommand);
+       void EnableOutputLog(const bool bEnableLog);
+diff --git a/hardware/OnkyoAVTCP.cpp b/hardware/OnkyoAVTCP.cpp
+index 88766032..9f386c01 100644
+--- a/hardware/OnkyoAVTCP.cpp
++++ b/hardware/OnkyoAVTCP.cpp
+@@ -676,6 +676,10 @@ void OnkyoAVTCP::ParseData(const unsigned char *pData, int Len)
+       m_pPartialPkt = new_partial;
+ }
++bool OnkyoAVTCP::CustomCommand(const uint64_t idx, const std::string &sCommand)
++{
++      return SendPacket(sCommand.c_str());
++}
+ //Webserver helpers
+ namespace http {
+diff --git a/hardware/OnkyoAVTCP.h b/hardware/OnkyoAVTCP.h
+index 90dbce51..133f4876 100644
+--- a/hardware/OnkyoAVTCP.h
++++ b/hardware/OnkyoAVTCP.h
+@@ -21,6 +21,7 @@ private:
+       int m_retrycntr;
+       bool StartHardware();
+       bool StopHardware();
++      bool CustomCommand(uint64_t idx, const std::string &sCommand);
+       unsigned char *m_pPartialPkt;
+       int m_PPktLen;
+       void ReceiveMessage(const char *pData, int Len);
+diff --git a/main/EventSystem.cpp b/main/EventSystem.cpp
+index cdd3968b..19562d37 100644
+--- a/main/EventSystem.cpp
++++ b/main/EventSystem.cpp
+@@ -2481,6 +2481,21 @@ bool CEventSystem::parseBlocklyActions(const _tEventItem &item)
+                       actionsDone = true;
+                       continue;
+               }
++              else if (deviceName.find("CustomCommand:") == 0)
++              {
++                      int idx = atoi(deviceName.substr(14).c_str());
++                      float afterTimerSeconds = 0;
++                      size_t aFind = doWhat.find(" AFTER ");
++                      if ((aFind > 0) && (aFind != std::string::npos)) {
++                              std::string delayString = doWhat.substr(aFind + 7);
++                              afterTimerSeconds = static_cast<float>(atof(delayString.c_str()));
++                              doWhat = doWhat.substr(0, aFind);
++                              StripQuotes(doWhat);
++                      }
++                      m_sql.AddTaskItem(_tTaskItem::CustomCommand(afterTimerSeconds, idx, doWhat));
++                      actionsDone = true;
++                      continue;
++              }
+               else
+               {
+                       _log.Log(LOG_ERROR, "EventSystem: Unknown action sequence! (%s)", csubstr.c_str());
+@@ -2623,6 +2638,19 @@ bool CEventSystem::PythonScheduleEvent(std::string ID, const std::string &Action
+                               return false;
+               }
++              return true;
++      } else if(ID.find("CustomCommand:") == 0) {
++              int idx = atoi(ID.substr(14).c_str());
++              std::string doWhat = std::string(Action);
++              float afterTimerSeconds = 0;
++              size_t aFind = Action.find(" AFTER ");
++              if ((aFind > 0) && (aFind != std::string::npos)) {
++                      std::string delayString = doWhat.substr(aFind + 7);
++                      doWhat = doWhat.substr(0, aFind);
++                      afterTimerSeconds = static_cast<float>(atof(delayString.c_str()));
++                      StripQuotes(doWhat);
++              }
++              m_sql.AddTaskItem(_tTaskItem::CustomCommand(afterTimerSeconds, idx, doWhat));
+               return true;
+       }
+       return ScheduleEvent(ID, Action,eventName);
+@@ -3540,6 +3568,20 @@ bool CEventSystem::processLuaCommand(lua_State *lua_state, const std::string &fi
+                       return false;
+               }
+       }
++      else if (lCommand.find("CustomCommand:") == 0)
++      {
++              int idx = atoi(lCommand.substr(14).c_str());
++              std::string luaString = lua_tostring(lua_state, -1);
++              float afterTimerSeconds = 0;
++              size_t aFind = luaString.find(" AFTER ");
++              if ((aFind > 0) && (aFind != std::string::npos)) {
++                      std::string delayString = luaString.substr(aFind + 7);
++                      afterTimerSeconds = static_cast<float>(atof(delayString.c_str()));
++                      luaString = luaString.substr(0, aFind);
++                      StripQuotes(luaString);
++              }
++              m_sql.AddTaskItem(_tTaskItem::CustomCommand(afterTimerSeconds, idx, luaString));
++      }
+       else
+       {
+               if (ScheduleEvent(lua_tostring(lua_state, -2), lua_tostring(lua_state, -1), filename)) {
+@@ -3557,6 +3599,21 @@ void CEventSystem::report_errors(lua_State *L, int status, std::string filename)
+       }
+ }
++bool CEventSystem::CustomCommand(const uint64_t idx, const std::string &sCommand)
++{
++      std::vector<std::vector<std::string> > result;
++      result = m_sql.safe_query("SELECT H.ID FROM DeviceStatus DS, Hardware H WHERE (DS.ID=='%u') AND (DS.HardwareID == H.ID)", idx);
++      if (result.size() != 1)
++              return false;
++
++      int HardwareID = atoi(result[0][0].c_str());
++      CDomoticzHardwareBase *pHardware = m_mainworker.GetHardware(HardwareID);
++      if (!pHardware)
++              return false;
++
++      return pHardware->CustomCommand(idx, sCommand);
++}
++
+ void CEventSystem::UpdateDevice(const uint64_t idx, const int nValue, const std::string &sValue, const int Protected, const bool bEventTrigger)
+ {
+       //Get device parameters
+diff --git a/main/EventSystem.h b/main/EventSystem.h
+index d154c4e8..cfebe2cc 100644
+--- a/main/EventSystem.h
++++ b/main/EventSystem.h
+@@ -131,6 +131,7 @@ public:
+       bool GetEventTrigger(const uint64_t ulDevID, const _eReason reason, const bool bEventTrigger);
+       void SetEventTrigger(const uint64_t ulDevID, const _eReason reason, const float fDelayTime);
+       void UpdateDevice(const uint64_t idx, const int nValue, const std::string &sValue, const int Protected, const bool bEventTrigger = false);
++      bool CustomCommand(const uint64_t idx, const std::string &sCommand);
+       void TriggerURL(const std::string &result, const std::vector<std::string> &headerData, const std::string &callback);
+diff --git a/main/SQLHelper.cpp b/main/SQLHelper.cpp
+index 7a46f021..0ea71d54 100644
+--- a/main/SQLHelper.cpp
++++ b/main/SQLHelper.cpp
+@@ -3401,6 +3401,10 @@ void CSQLHelper::Do_Work()
+                       {
+                               m_mainworker.m_eventsystem.UpdateDevice(itt->_idx, itt->_nValue, itt->_sValue, itt->_HardwareID, (itt->_switchtype ? true : false));
+                       }
++                      else if (itt->_ItemType == TITEM_CUSTOM_COMMAND)
++                      {
++                              m_mainworker.m_eventsystem.CustomCommand(itt->_idx, itt->_command);
++                      }
+                       ++itt;
+               }
+diff --git a/main/SQLHelper.h b/main/SQLHelper.h
+index c0d40580..8ad60e3d 100644
+--- a/main/SQLHelper.h
++++ b/main/SQLHelper.h
+@@ -49,7 +49,8 @@ enum _eTaskItemType
+       TITEM_SEND_NOTIFICATION,
+       TITEM_SET_SETPOINT,
+       TITEM_SEND_IFTTT_TRIGGER,
+-      TITEM_UPDATEDEVICE
++      TITEM_UPDATEDEVICE,
++      TITEM_CUSTOM_COMMAND,
+ };
+ struct _tTaskItem
+@@ -261,6 +262,18 @@ struct _tTaskItem
+               tItem._sValue = varvalue;
+               tItem._command = mode;
+               tItem._sUntil = until;
++
++              if (DelayTime)
++                      getclock(&tItem._DelayTimeBegin);
++              return tItem;
++      }
++      static _tTaskItem CustomCommand(const float DelayTime, const uint64_t idx, const std::string &cmdstr)
++      {
++              _tTaskItem tItem;
++              tItem._ItemType = TITEM_CUSTOM_COMMAND;
++              tItem._DelayTime = DelayTime;
++              tItem._idx = idx;
++              tItem._command = cmdstr;
+               if (DelayTime)
+                       getclock(&tItem._DelayTimeBegin);
+               return tItem;
+-- 
+2.17.0
+