<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>Thema "Viessmann-API integration into RaspberryMatic / HomeMatic CCU3 + RegaScript" in Getting started programming with the Viessmann API</title>
    <link>https://community.viessmann.de/t5/Getting-started-programming-with/Viessmann-API-integration-into-RaspberryMatic-HomeMatic-CCU3/m-p/423014#M518</link>
    <description>&lt;P&gt;Hi all,&lt;/P&gt;&lt;P&gt;for all looking for some starting point to get the Viessmann-API integrated into the RaspberryMatic, please find below the script I am using for my heat-pump AWB201.E10 2C.&lt;BR /&gt;You need&amp;nbsp;&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;to set "debug" to true (1) to get the fundamental systemvariables created once and&lt;/LI&gt;&lt;LI&gt;to add the code-challenge either from the examples given by Viessmann or create your own.&lt;/LI&gt;&lt;LI&gt;Last, you need to provide your personal login-credentials here (at least once to get the refresh_token). The client-id you get from here after initial registration: &lt;A href="https://app.developer.viessmann.com/" target="_blank"&gt;https://app.developer.viessmann.com/&lt;/A&gt; go with the standard recommendations and you are fine.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Since I have the system up-and-running and did delete my credentials and some bits around this I could not test it from scratch.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="java"&gt;!-- HeatPump AllInOne -- v0.10 --
!_________________________________________________________________________________________________
! All-in-One script to generate tokens and get parameters of heat-pump form Viessmann-API
! ------------------------------------------------------------------------------------------
! VitoHeld
! ******************************************************************************************
!
! --------------------------------------------------------------------------------------------------
! https://documentation.viessmann.com/static/getting-started
! --------------------------------------------------------------------------------------------------

! set to 'true' for first execution to create system variables
boolean debug                = true; 
boolean initializeAllTokens  = false;
boolean renewalAccessToken   = false;
integer timeout              = 5;
! access data Viessmann acount
! https://account.viessmann.com/
string client_id = ""; ! enter your client-id 
string email     = ""; ! enter your login email
string password  = ""; ! enter your password

string AUTHORIZE_URL   = "https://iam.viessmann.com/idp/v3/authorize";
string TOKEN_URL       = "https://iam.viessmann.com/idp/v3/token";
string FEATURE_URL     = "https://api.viessmann.com/iot/v1/equipment/installations";
string EVENT_URL       = "https://api.viessmann.com/iot/v2/events-history/installations";
string REDIRECT_URI    = "http://localhost:4200/";
string VIESSMANN_SCOPE = "IoT%20User%20offline_access"; 
! Code Challenge using S256
! https://developer.pingidentity.com/en/tools/pkce-code-generator.html
string code_verifier    = ""; ! enter your personlized code-challenge here; on issues: use example provided by Viessmann
string code_challenge   = ""; ! enter your personlized code-challenge here; on issues: use example provided by Viessmann

string stdout           = "";
string stderr           = "";
string func             = "";
string access_token     = "";
string refresh_token    = "";
string api_error        = "";
string hpStatus         = "";

time now = system.Date("%F %X").ToTime();

! if not present: create system variables to store access credentials; also hpStatus - but not for features
if (debug) {
    object svObjects = dom.GetObject(ID_SYSTEM_VARIABLES);
    string svName = "hpIDInstallation"; object svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Installation-ID Viessmann"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpIDGateway"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Gateway-ID Viessmann"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpIDDevice"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Device-ID Viessmann"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpTokenAccess"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Access-Token Viessmann-API"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpTokenRefresh"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Refresh-Token Viessmann-API"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpStatus"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Status Wärmepumpe/API"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "sysStatus"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("CCU3 Status"); svObj.Name(svName); svObj.ValueType(ivtBinary); svObj.ValueSubType(istBool); svObj.ValueName0("Normalbetrieb"); svObj.ValueName1("Reboot"); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(false); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    if (debug) { WriteLine("...sytem variables created/checked successfully."); }
}

! exit while rebooting... but reset deviceID in order to active token-generation 
if (dom.GetObject(ID_SYSTEM_VARIABLES).Get("sysStatus")) { if (dom.GetObject(ID_SYSTEM_VARIABLES).Get("sysStatus").Value()) { dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").State(""); quit; } }

! get information if already available
string installationID = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDInstallation").Value();
string gatewayID      = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDGateway").Value();
string deviceID       = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").Value();
if (debug) { WriteLine("installationID: " # installationID # "\ngatewayID: " # gatewayID # "\n(deviceID: " # deviceID # ")"); }

! TTL = 3600s for access_token -&amp;gt; revoke script e.g. every 3333 seconds
string access_token = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").Value();
if (access_token &amp;lt;&amp;gt; "")
{
    object sysvar = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess");
    time lastChange = sysvar.Timestamp();
    integer atValidity = 3600 - (now - lastChange).ToInteger();
    if (debug) { WriteLine("Access-Token last update: " # lastChange # " (still valid for: " # atValidity # " sec)"); }
}
elseif (debug) { WriteLine("No Access-Token found - will get generated..."); }

! TTL = 15552000 for refresh_token
string refresh_token = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh").Value();
if (refresh_token &amp;lt;&amp;gt; "") 
{
    sysvar = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh");
    lastChange = sysvar.Timestamp();
    integer rtValidity = 15552000 - (now - lastChange).ToInteger();
    if (debug) { WriteLine("Refresh-Token last update: " # lastChange # " (still valid for: " # rtValidity # " sec)"); }

}
elseif (debug) { WriteLine("No Refresh-Token found - will get generated..."); }

! revoke tokens as required
if ((rtValidity &amp;lt; 333333) || (deviceID == "")) { initializeAllTokens = true; } elseif (atValidity &amp;lt; 333) { renewalAccessToken = true; }
! if not all tokens and IDs are given, generate or get them
if ((installationID == "") || (gatewayID == "") || (deviceID == "") || (access_token == "") || (refresh_token == "")) { initializeAllTokens = true; }

! initialize or renew tokens only if required
if (initializeAllTokens) 
{
    ! Step 1: Authorization request
    func = AUTHORIZE_URL # "?client_id=" # client_id # "&amp;amp;redirect_uri=" # REDIRECT_URI # "&amp;amp;scope=" # VIESSMANN_SCOPE # "&amp;amp;response_type=code&amp;amp;code_challenge_method=S256&amp;amp;code_challenge=" # code_challenge;
    func = "curl -m " # timeout # " -u '" # email # ":" # password # "' '" # func # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }
    string code = stdout.Substr(stdout.Find("?code=")+6, stdout.Length()-(stdout.Find("?code=")+6));
    code = code.Substr(0, code.Find("\""));
    if (debug) { WriteLine("Code = " # code); }

    ! Step 2: Authorization code exchange
    func = "curl -m " # timeout # " -X POST '" # TOKEN_URL # "' -H 'Content-Type: application/x-www-form-urlencoded' -d 'client_id=" # client_id # "&amp;amp;redirect_uri=" # REDIRECT_URI # "&amp;amp;grant_type=authorization_code&amp;amp;code_verifier=" # code_verifier # "&amp;amp;code=" # code # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! TTL (time to life) of access_token is: 3600s = 1 hour
    access_token = stdout.Substr(stdout.Find("access_token")+15, stdout.Length()-(stdout.Find("access_token")+15));
    access_token = access_token.Substr(0, access_token.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").State(access_token);

    ! TTL (time to life) of refresh_token is: 15552000s = 180 days
    refresh_token = stdout.Substr(stdout.Find("refresh_token")+16, stdout.Length()-(stdout.Find("refresh_token")+16));
    refresh_token = refresh_token.Substr(0, refresh_token.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh").State(refresh_token);

    if (debug) { WriteLine("Access-Token: " # access_token); WriteLine("Refresh-Token: " # refresh_token); }
}
elseif (renewalAccessToken)
{
    ! Refreshing an access token
    ! curl -X POST "https://iam.viessmann.com/idp/v3/token" -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=refresh_token&amp;amp;client_id=my_oauth_client_id&amp;amp;refresh_token=083ed7fe41a619242df5978190fd11b5"

    access_token  = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").Value();
    refresh_token = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh").Value();
    if (debug) { WriteLine("Refresh-Token: " # refresh_token); }

    ! renewal of access_token
    func = "curl -m " # timeout # " -X POST '" # TOKEN_URL # "' -H 'Content-Type: application/x-www-form-urlencoded' -d 'client_id=" # client_id # "&amp;amp;grant_type=refresh_token&amp;amp;refresh_token=" # refresh_token # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! TTL (time to life) of access_token is: 3600s = 1 hour
    access_token = stdout.Substr(stdout.Find("access_token")+15, stdout.Length()-(stdout.Find("access_token")+15));
    access_token = access_token.Substr(0, access_token.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").State(access_token);

    if (debug) { WriteLine("Access-Token: " # access_token); WriteLine("Refresh-Token: " # refresh_token); }
}

! ensure all IDs are available
if (deviceID == "")
{
    ! For using IoT features, you need essential three numbers of your heating device: installationID, gatewaySerial and deviceID. 
    ! curl -X GET "https://api.viessmann.com/iot/v1/equipment/installations?includeGateways=true" -H "Authorization: Bearer {{Acces_Token}}"
    func = "curl -m " # timeout # " -X GET '" # FEATURE_URL # "?includeGateways=true' -H 'content-type: application/json' -H 'authorization:Bearer " # access_token # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! get installationID 
    installationID = stdout.Substr(stdout.Find("id")+4, stdout.Length()-(stdout.Find("id")+4));
    installationID = installationID.Substr(0, installationID.Find(","));
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDInstallation").State(installationID);
    if (debug) { WriteLine("InstallationID = " # installationID); }

    ! get GatewayID
    gatewayID = stdout.Substr(stdout.Find("gatewaySerial")+16, stdout.Length()-(stdout.Find("gatewaySerial")+16));
    gatewayID = gatewayID.Substr(0, gatewayID.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDGateway").State(gatewayID);
    if (debug) { WriteLine("GatewayID = " # gatewayID); }

    ! curl -X GET https://api.viessmann.com/iot/v1/equipment/installations/{{installationID}}/gateways/{{gatewaySerial}}/devices
    func = "curl -m " # timeout # " -X GET '" # FEATURE_URL # "/" # installationID # "/gateways/" # gatewayID # "/devices' -H 'content-type: application/json' -H 'authorization:Bearer " # access_token # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! get DeviceID
    string deviceID = stdout.Substr(stdout.Find(',"id":"0","boilerSerial":"')+26, stdout.Length()-(stdout.Find(',"id":"0","boilerSerial":"')+26));
    ! worked until 01.02.2024: string deviceID = stdout.Substr(stdout.Find("boilerSerial")+15, stdout.Length()-(stdout.Find("boilerSerial")+15));
    deviceID = deviceID.Substr(0, deviceID.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").State(deviceID);
    if (debug) { WriteLine("DeviceID = " # deviceID); }
}

! get overall status of heatpump
func = "curl -m " # timeout # " -X GET '" # FEATURE_URL # "' -H 'content-type: application/json' -H 'authorization:Bearer " # access_token # "'";
system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
! exit on error, provide API-return code as hpStatus
if (stdout.Find('{"viErrorId":') == 0) { if (stdout.Find(',"error":') &amp;gt; 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = api_error.Substr(0, api_error.Find("}")-1); } elseif (stdout.Find(',"errorType":') &amp;gt; 0) { api_error = stdout.Substr(stdout.Find(',"errorType":')+14, stdout.Length()-(stdout.Find(',"errorType":')+14)); api_error = api_error.Substr(0, api_error.Find('",')); } else { api_error = "unknown"; } api_error = "API-Error: " # api_error; if (api_error.Find("TOKEN") &amp;gt; 0) { dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").State(""); } if (debug) { WriteLine("hpStatus := " # api_error); } dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }
if (stdout.Find('{"cursor":{"next":') &amp;lt;&amp;gt; 0) { api_error = "unexpected content returned by API-call"; if (stderr.Find("Operation timed out after") &amp;gt; 0) { api_error = api_error # " (timeout/vi-maintenance)"; } dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); if (debug) { WriteLine(api_error); } quit; }
hpStatus = stdout.Substr(stdout.Find("aggregatedStatus")+19, stdout.Length()-(stdout.Find("aggregatedStatus")+19));
hpStatus = hpStatus.Substr(0, hpStatus.Find(",")-1);
dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(hpStatus);
if (debug) { WriteLine("hpStatus = " # hpStatus); }
&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Thu, 15 Feb 2024 16:30:31 GMT</pubDate>
    <dc:creator>VitoHeld</dc:creator>
    <dc:date>2024-02-15T16:30:31Z</dc:date>
    <item>
      <title>Viessmann-API integration into RaspberryMatic / HomeMatic CCU3 + RegaScript</title>
      <link>https://community.viessmann.de/t5/Getting-started-programming-with/Viessmann-API-integration-into-RaspberryMatic-HomeMatic-CCU3/m-p/423014#M518</link>
      <description>&lt;P&gt;Hi all,&lt;/P&gt;&lt;P&gt;for all looking for some starting point to get the Viessmann-API integrated into the RaspberryMatic, please find below the script I am using for my heat-pump AWB201.E10 2C.&lt;BR /&gt;You need&amp;nbsp;&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;to set "debug" to true (1) to get the fundamental systemvariables created once and&lt;/LI&gt;&lt;LI&gt;to add the code-challenge either from the examples given by Viessmann or create your own.&lt;/LI&gt;&lt;LI&gt;Last, you need to provide your personal login-credentials here (at least once to get the refresh_token). The client-id you get from here after initial registration: &lt;A href="https://app.developer.viessmann.com/" target="_blank"&gt;https://app.developer.viessmann.com/&lt;/A&gt; go with the standard recommendations and you are fine.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Since I have the system up-and-running and did delete my credentials and some bits around this I could not test it from scratch.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="java"&gt;!-- HeatPump AllInOne -- v0.10 --
!_________________________________________________________________________________________________
! All-in-One script to generate tokens and get parameters of heat-pump form Viessmann-API
! ------------------------------------------------------------------------------------------
! VitoHeld
! ******************************************************************************************
!
! --------------------------------------------------------------------------------------------------
! https://documentation.viessmann.com/static/getting-started
! --------------------------------------------------------------------------------------------------

! set to 'true' for first execution to create system variables
boolean debug                = true; 
boolean initializeAllTokens  = false;
boolean renewalAccessToken   = false;
integer timeout              = 5;
! access data Viessmann acount
! https://account.viessmann.com/
string client_id = ""; ! enter your client-id 
string email     = ""; ! enter your login email
string password  = ""; ! enter your password

string AUTHORIZE_URL   = "https://iam.viessmann.com/idp/v3/authorize";
string TOKEN_URL       = "https://iam.viessmann.com/idp/v3/token";
string FEATURE_URL     = "https://api.viessmann.com/iot/v1/equipment/installations";
string EVENT_URL       = "https://api.viessmann.com/iot/v2/events-history/installations";
string REDIRECT_URI    = "http://localhost:4200/";
string VIESSMANN_SCOPE = "IoT%20User%20offline_access"; 
! Code Challenge using S256
! https://developer.pingidentity.com/en/tools/pkce-code-generator.html
string code_verifier    = ""; ! enter your personlized code-challenge here; on issues: use example provided by Viessmann
string code_challenge   = ""; ! enter your personlized code-challenge here; on issues: use example provided by Viessmann

string stdout           = "";
string stderr           = "";
string func             = "";
string access_token     = "";
string refresh_token    = "";
string api_error        = "";
string hpStatus         = "";

time now = system.Date("%F %X").ToTime();

! if not present: create system variables to store access credentials; also hpStatus - but not for features
if (debug) {
    object svObjects = dom.GetObject(ID_SYSTEM_VARIABLES);
    string svName = "hpIDInstallation"; object svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Installation-ID Viessmann"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpIDGateway"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Gateway-ID Viessmann"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpIDDevice"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Device-ID Viessmann"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpTokenAccess"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Access-Token Viessmann-API"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpTokenRefresh"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Refresh-Token Viessmann-API"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "hpStatus"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("Status Wärmepumpe/API"); svObj.Name(svName); svObj.ValueType(ivtString); svObj.ValueSubType(istChar8859); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(""); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    svName = "sysStatus"; svObj  = dom.GetObject(svName);
    if (!svObj) { svObj = dom.CreateObject(OT_VARDP); svObjects.Add(svObj.ID()); svObj.DPInfo("CCU3 Status"); svObj.Name(svName); svObj.ValueType(ivtBinary); svObj.ValueSubType(istBool); svObj.ValueName0("Normalbetrieb"); svObj.ValueName1("Reboot"); svObj.ValueUnit(""); svObj.DPArchive(true); svObj.State(false); svObj.Internal(false); svObj.Visible(true); dom.RTUpdate(false); }
    if (debug) { WriteLine("...sytem variables created/checked successfully."); }
}

! exit while rebooting... but reset deviceID in order to active token-generation 
if (dom.GetObject(ID_SYSTEM_VARIABLES).Get("sysStatus")) { if (dom.GetObject(ID_SYSTEM_VARIABLES).Get("sysStatus").Value()) { dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").State(""); quit; } }

! get information if already available
string installationID = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDInstallation").Value();
string gatewayID      = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDGateway").Value();
string deviceID       = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").Value();
if (debug) { WriteLine("installationID: " # installationID # "\ngatewayID: " # gatewayID # "\n(deviceID: " # deviceID # ")"); }

! TTL = 3600s for access_token -&amp;gt; revoke script e.g. every 3333 seconds
string access_token = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").Value();
if (access_token &amp;lt;&amp;gt; "")
{
    object sysvar = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess");
    time lastChange = sysvar.Timestamp();
    integer atValidity = 3600 - (now - lastChange).ToInteger();
    if (debug) { WriteLine("Access-Token last update: " # lastChange # " (still valid for: " # atValidity # " sec)"); }
}
elseif (debug) { WriteLine("No Access-Token found - will get generated..."); }

! TTL = 15552000 for refresh_token
string refresh_token = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh").Value();
if (refresh_token &amp;lt;&amp;gt; "") 
{
    sysvar = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh");
    lastChange = sysvar.Timestamp();
    integer rtValidity = 15552000 - (now - lastChange).ToInteger();
    if (debug) { WriteLine("Refresh-Token last update: " # lastChange # " (still valid for: " # rtValidity # " sec)"); }

}
elseif (debug) { WriteLine("No Refresh-Token found - will get generated..."); }

! revoke tokens as required
if ((rtValidity &amp;lt; 333333) || (deviceID == "")) { initializeAllTokens = true; } elseif (atValidity &amp;lt; 333) { renewalAccessToken = true; }
! if not all tokens and IDs are given, generate or get them
if ((installationID == "") || (gatewayID == "") || (deviceID == "") || (access_token == "") || (refresh_token == "")) { initializeAllTokens = true; }

! initialize or renew tokens only if required
if (initializeAllTokens) 
{
    ! Step 1: Authorization request
    func = AUTHORIZE_URL # "?client_id=" # client_id # "&amp;amp;redirect_uri=" # REDIRECT_URI # "&amp;amp;scope=" # VIESSMANN_SCOPE # "&amp;amp;response_type=code&amp;amp;code_challenge_method=S256&amp;amp;code_challenge=" # code_challenge;
    func = "curl -m " # timeout # " -u '" # email # ":" # password # "' '" # func # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }
    string code = stdout.Substr(stdout.Find("?code=")+6, stdout.Length()-(stdout.Find("?code=")+6));
    code = code.Substr(0, code.Find("\""));
    if (debug) { WriteLine("Code = " # code); }

    ! Step 2: Authorization code exchange
    func = "curl -m " # timeout # " -X POST '" # TOKEN_URL # "' -H 'Content-Type: application/x-www-form-urlencoded' -d 'client_id=" # client_id # "&amp;amp;redirect_uri=" # REDIRECT_URI # "&amp;amp;grant_type=authorization_code&amp;amp;code_verifier=" # code_verifier # "&amp;amp;code=" # code # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! TTL (time to life) of access_token is: 3600s = 1 hour
    access_token = stdout.Substr(stdout.Find("access_token")+15, stdout.Length()-(stdout.Find("access_token")+15));
    access_token = access_token.Substr(0, access_token.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").State(access_token);

    ! TTL (time to life) of refresh_token is: 15552000s = 180 days
    refresh_token = stdout.Substr(stdout.Find("refresh_token")+16, stdout.Length()-(stdout.Find("refresh_token")+16));
    refresh_token = refresh_token.Substr(0, refresh_token.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh").State(refresh_token);

    if (debug) { WriteLine("Access-Token: " # access_token); WriteLine("Refresh-Token: " # refresh_token); }
}
elseif (renewalAccessToken)
{
    ! Refreshing an access token
    ! curl -X POST "https://iam.viessmann.com/idp/v3/token" -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=refresh_token&amp;amp;client_id=my_oauth_client_id&amp;amp;refresh_token=083ed7fe41a619242df5978190fd11b5"

    access_token  = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").Value();
    refresh_token = dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenRefresh").Value();
    if (debug) { WriteLine("Refresh-Token: " # refresh_token); }

    ! renewal of access_token
    func = "curl -m " # timeout # " -X POST '" # TOKEN_URL # "' -H 'Content-Type: application/x-www-form-urlencoded' -d 'client_id=" # client_id # "&amp;amp;grant_type=refresh_token&amp;amp;refresh_token=" # refresh_token # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! TTL (time to life) of access_token is: 3600s = 1 hour
    access_token = stdout.Substr(stdout.Find("access_token")+15, stdout.Length()-(stdout.Find("access_token")+15));
    access_token = access_token.Substr(0, access_token.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpTokenAccess").State(access_token);

    if (debug) { WriteLine("Access-Token: " # access_token); WriteLine("Refresh-Token: " # refresh_token); }
}

! ensure all IDs are available
if (deviceID == "")
{
    ! For using IoT features, you need essential three numbers of your heating device: installationID, gatewaySerial and deviceID. 
    ! curl -X GET "https://api.viessmann.com/iot/v1/equipment/installations?includeGateways=true" -H "Authorization: Bearer {{Acces_Token}}"
    func = "curl -m " # timeout # " -X GET '" # FEATURE_URL # "?includeGateways=true' -H 'content-type: application/json' -H 'authorization:Bearer " # access_token # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! get installationID 
    installationID = stdout.Substr(stdout.Find("id")+4, stdout.Length()-(stdout.Find("id")+4));
    installationID = installationID.Substr(0, installationID.Find(","));
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDInstallation").State(installationID);
    if (debug) { WriteLine("InstallationID = " # installationID); }

    ! get GatewayID
    gatewayID = stdout.Substr(stdout.Find("gatewaySerial")+16, stdout.Length()-(stdout.Find("gatewaySerial")+16));
    gatewayID = gatewayID.Substr(0, gatewayID.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDGateway").State(gatewayID);
    if (debug) { WriteLine("GatewayID = " # gatewayID); }

    ! curl -X GET https://api.viessmann.com/iot/v1/equipment/installations/{{installationID}}/gateways/{{gatewaySerial}}/devices
    func = "curl -m " # timeout # " -X GET '" # FEATURE_URL # "/" # installationID # "/gateways/" # gatewayID # "/devices' -H 'content-type: application/json' -H 'authorization:Bearer " # access_token # "'";
    system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
    ! exit on error, provide API-return code as hpStatus
    if (stdout.Find('{"viErrorId":') == 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = "API-Error: " # api_error.Substr(0, api_error.Find("}")-1); dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }

    ! get DeviceID
    string deviceID = stdout.Substr(stdout.Find(',"id":"0","boilerSerial":"')+26, stdout.Length()-(stdout.Find(',"id":"0","boilerSerial":"')+26));
    ! worked until 01.02.2024: string deviceID = stdout.Substr(stdout.Find("boilerSerial")+15, stdout.Length()-(stdout.Find("boilerSerial")+15));
    deviceID = deviceID.Substr(0, deviceID.Find(",")-1);
    dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").State(deviceID);
    if (debug) { WriteLine("DeviceID = " # deviceID); }
}

! get overall status of heatpump
func = "curl -m " # timeout # " -X GET '" # FEATURE_URL # "' -H 'content-type: application/json' -H 'authorization:Bearer " # access_token # "'";
system.Exec(func, &amp;amp;stdout, &amp;amp;stderr); 
! exit on error, provide API-return code as hpStatus
if (stdout.Find('{"viErrorId":') == 0) { if (stdout.Find(',"error":') &amp;gt; 0) { api_error = stdout.Substr(stdout.Find(',"error":')+10, stdout.Length()-(stdout.Find(',"error":')+10)); api_error = api_error.Substr(0, api_error.Find("}")-1); } elseif (stdout.Find(',"errorType":') &amp;gt; 0) { api_error = stdout.Substr(stdout.Find(',"errorType":')+14, stdout.Length()-(stdout.Find(',"errorType":')+14)); api_error = api_error.Substr(0, api_error.Find('",')); } else { api_error = "unknown"; } api_error = "API-Error: " # api_error; if (api_error.Find("TOKEN") &amp;gt; 0) { dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpIDDevice").State(""); } if (debug) { WriteLine("hpStatus := " # api_error); } dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); quit; }
if (stdout.Find('{"cursor":{"next":') &amp;lt;&amp;gt; 0) { api_error = "unexpected content returned by API-call"; if (stderr.Find("Operation timed out after") &amp;gt; 0) { api_error = api_error # " (timeout/vi-maintenance)"; } dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(api_error); if (debug) { WriteLine(api_error); } quit; }
hpStatus = stdout.Substr(stdout.Find("aggregatedStatus")+19, stdout.Length()-(stdout.Find("aggregatedStatus")+19));
hpStatus = hpStatus.Substr(0, hpStatus.Find(",")-1);
dom.GetObject(ID_SYSTEM_VARIABLES).Get("hpStatus").State(hpStatus);
if (debug) { WriteLine("hpStatus = " # hpStatus); }
&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 15 Feb 2024 16:30:31 GMT</pubDate>
      <guid>https://community.viessmann.de/t5/Getting-started-programming-with/Viessmann-API-integration-into-RaspberryMatic-HomeMatic-CCU3/m-p/423014#M518</guid>
      <dc:creator>VitoHeld</dc:creator>
      <dc:date>2024-02-15T16:30:31Z</dc:date>
    </item>
  </channel>
</rss>

