From 9d2310fb8c254b0357f1ec3978d73ca8bbbaed68 Mon Sep 17 00:00:00 2001 From: TrystanLea Date: Tue, 9 Mar 2021 14:10:41 +0000 Subject: [PATCH 01/10] demandprofile forecast --- .../demandshaper_controller.php | 6 +- demandshaper-module/demandshaper_model.php | 3 +- demandshaper_run.php | 4 +- forecasts/demandprofile.php | 55 +++++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 forecasts/demandprofile.php diff --git a/demandshaper-module/demandshaper_controller.php b/demandshaper-module/demandshaper_controller.php index 2bb143d..e2fe0e6 100644 --- a/demandshaper-module/demandshaper_controller.php +++ b/demandshaper-module/demandshaper_controller.php @@ -97,14 +97,14 @@ function demandshaper_controller() case "forecast": if (isset($_POST['config'])) { $config = json_decode($_POST['config']); - return $demandshaper->get_combined_forecast($config,$timezone); + return $demandshaper->get_combined_forecast($config,$timezone,$session["userid"]); } break; case "schedule": if (isset($_POST['config'])) { $config = json_decode($_POST['config']); - $combined = $demandshaper->get_combined_forecast($config,$timezone); + $combined = $demandshaper->get_combined_forecast($config,$timezone,$session["userid"]); $period = (int) post('period'); // period in seconds $end = (int) post('end'); // end timestamp @@ -192,7 +192,7 @@ function demandshaper_controller() $schedule = $demandshaper->device_class[$device_type]->auto_update_timeleft($schedule); // 1. Compile combined forecast - $combined = $demandshaper->get_combined_forecast($schedule->settings->forecast_config,$timezone); + $combined = $demandshaper->get_combined_forecast($schedule->settings->forecast_config,$timezone,$session["userid"]); // 2. Calculate forecast min/max require_once "$linked_modules_dir/demandshaper/lib/scheduler2.php"; $combined = forecast_calc_min_max($combined); diff --git a/demandshaper-module/demandshaper_model.php b/demandshaper-module/demandshaper_model.php index f07a291..d7ad670 100644 --- a/demandshaper-module/demandshaper_model.php +++ b/demandshaper-module/demandshaper_model.php @@ -285,10 +285,11 @@ public function get_forecast_list() { return $forecast_list; } - public function get_combined_forecast($config,$timezone) { + public function get_combined_forecast($config,$timezone,$userid) { $params = new stdClass(); $params->timezone = $timezone; + $params->userid = $userid; // 1. Set desired forecast interval // This will downsample or upsample original forecast diff --git a/demandshaper_run.php b/demandshaper_run.php index aea662d..19fc99e 100644 --- a/demandshaper_run.php +++ b/demandshaper_run.php @@ -243,7 +243,7 @@ $schedule = $device_class[$device_type]->auto_update_timeleft($schedule); // 1. Compile combined forecast - $combined = $demandshaper->get_combined_forecast($schedule->settings->forecast_config,$timezone); + $combined = $demandshaper->get_combined_forecast($schedule->settings->forecast_config,$timezone,$userid); // 2. Calculate forecast min/max $combined = forecast_calc_min_max($combined); // 3. Calculate schedule @@ -259,7 +259,7 @@ } else if ($schedule->settings->ctrlmode=="timer") { // 1. Compile combined forecast, not really needed here but we need forecast start and end times - $combined = $demandshaper->get_combined_forecast($schedule->settings->forecast_config,$timezone); + $combined = $demandshaper->get_combined_forecast($schedule->settings->forecast_config,$timezone,$userid); // 2. Calculate periods from timers $schedule->runtime->periods = schedule_timer($combined->start,$combined->end,$schedule->settings->timer,$timezone); // 3. Log output for debug diff --git a/forecasts/demandprofile.php b/forecasts/demandprofile.php new file mode 100644 index 0000000..40662e8 --- /dev/null +++ b/forecasts/demandprofile.php @@ -0,0 +1,55 @@ +"Demand Profile", + "name"=>"Demand Profile", + "params"=>array( + "device_name"=>array("name"=>"Device name","type"=>"text") + ) + ); +} + +function get_forecast_demandprofile($redis,$params) +{ + $timezone = new DateTimeZone($params->timezone); + $forecast_interval = 1800; + + if ($demandshaper_devices_json = $redis->get("demandshaper:schedules:".$params->userid)) { + $demandshaper_devices = json_decode($demandshaper_devices_json); + } + + $device_schedules = array(); + foreach ($demandshaper_devices as $name=>$device) { + if ($name!=$params->device_name) { + $device_schedules[] = $device->runtime->periods; + } + } + + $profile = array(); + for ($time=$params->start; $time<$params->end; $time+=$params->interval) { + $forecast_time = floor($time / $forecast_interval) * $forecast_interval; + + $status = 0; + foreach ($device_schedules as $device_schedule) { + foreach ($device_schedule as $period) { + $start = $period->start[0]; + $end = $period->end[0]; + if ($time>=$start && $time<$end) { + $status += 10; + } + } + } + + $profile[] = $status; + } + + $result = new stdClass(); + $result->start = $params->start; + $result->end = $params->end; + $result->interval = $params->interval; + $result->profile = $profile; + $result->optimise = MIN; + return $result; +} From 0c913f30eb9388ca4691f71d1038367a9ee9477f Mon Sep 17 00:00:00 2001 From: TrystanLea Date: Tue, 9 Mar 2021 15:00:47 +0000 Subject: [PATCH 02/10] option to use either post or get --- demandshaper-module/demandshaper_controller.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/demandshaper-module/demandshaper_controller.php b/demandshaper-module/demandshaper_controller.php index e2fe0e6..075a624 100644 --- a/demandshaper-module/demandshaper_controller.php +++ b/demandshaper-module/demandshaper_controller.php @@ -95,15 +95,15 @@ function demandshaper_controller() return $forecast_list; case "forecast": - if (isset($_POST['config'])) { - $config = json_decode($_POST['config']); + if (isset($_POST['config']) || isset($_GET['config'])) { + $config = json_decode(prop('config')); return $demandshaper->get_combined_forecast($config,$timezone,$session["userid"]); } break; case "schedule": - if (isset($_POST['config'])) { - $config = json_decode($_POST['config']); + if (isset($_POST['config']) || isset($_GET['config'])) { + $config = json_decode(prop('config')); $combined = $demandshaper->get_combined_forecast($config,$timezone,$session["userid"]); $period = (int) post('period'); // period in seconds @@ -123,11 +123,11 @@ function demandshaper_controller() break; case "timer": - if (isset($_POST['config'])) { - $timers = json_decode($_POST['config']); + if (isset($_POST['config']) || isset($_GET['config'])) { + $timers = json_decode(prop('config')); - $forecast_start = (int) post('forecast_start'); - $forecast_end = (int) post('forecast_end'); + $forecast_start = (int) prop('forecast_start'); + $forecast_end = (int) prop('forecast_end'); // Run schedule require_once "$linked_modules_dir/demandshaper/lib/scheduler2.php"; From 76188f98b559172973c62b52031e4d382c271ce1 Mon Sep 17 00:00:00 2001 From: TrystanLea Date: Tue, 9 Mar 2021 15:22:28 +0000 Subject: [PATCH 03/10] use prop --- demandshaper-module/demandshaper_controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demandshaper-module/demandshaper_controller.php b/demandshaper-module/demandshaper_controller.php index 075a624..118367f 100644 --- a/demandshaper-module/demandshaper_controller.php +++ b/demandshaper-module/demandshaper_controller.php @@ -106,8 +106,8 @@ function demandshaper_controller() $config = json_decode(prop('config')); $combined = $demandshaper->get_combined_forecast($config,$timezone,$session["userid"]); - $period = (int) post('period'); // period in seconds - $end = (int) post('end'); // end timestamp + $period = (int) prop('period'); // period in seconds + $end = (int) prop('end'); // end timestamp $interruptible = (int) post('interruptible'); // Run schedule From c8027889e08b31dff7aae2bd477ddff3c9262f31 Mon Sep 17 00:00:00 2001 From: TrystanLea Date: Wed, 19 May 2021 17:27:50 +0100 Subject: [PATCH 04/10] remoteaccess support --- demandshaper-module/Views/add_device.php | 1 + demandshaper-module/Views/main.php | 8 ++++- .../demandshaper_controller.php | 34 +++++++++++++------ demandshaper-module/js/get_device_state.js | 2 +- demandshaper-module/js/main.js | 18 +++++----- demandshaper_run.php | 2 +- forecasts/energylocal.php | 6 ++-- 7 files changed, 47 insertions(+), 24 deletions(-) diff --git a/demandshaper-module/Views/add_device.php b/demandshaper-module/Views/add_device.php index 70c6ee5..b191024 100644 --- a/demandshaper-module/Views/add_device.php +++ b/demandshaper-module/Views/add_device.php @@ -13,6 +13,7 @@ $v=2; global $path; $emoncmspath = $path; +if ($remoteaccess) $emoncmspath .= "remoteaccess/"; // if ($remoteaccess) $emoncmspath .= "remoteaccess/"; if (isset($_GET['apikey'])) $apikeystr = "&apikey=".$_GET['apikey']; else $apikeystr = ""; ?> diff --git a/demandshaper-module/Views/main.php b/demandshaper-module/Views/main.php index 538e98e..0cf701d 100644 --- a/demandshaper-module/Views/main.php +++ b/demandshaper-module/Views/main.php @@ -1,4 +1,9 @@ - + @@ -209,6 +214,7 @@ +
diff --git a/demandshaper-module/js/tasmota.js b/demandshaper-module/js/tasmota.js new file mode 100644 index 0000000..4aa91d5 --- /dev/null +++ b/demandshaper-module/js/tasmota.js @@ -0,0 +1,4 @@ + +function tasmota_update_UI_from_input_values(inputs) { +} + diff --git a/devices/tasmota.php b/devices/tasmota.php new file mode 100644 index 0000000..c021320 --- /dev/null +++ b/devices/tasmota.php @@ -0,0 +1,263 @@ +mqtt_client = $mqtt_client; + $this->basetopic = $basetopic; + } + + public function default_settings() { + $defaults = new stdClass(); + return $defaults; + } + + public function set_basetopic($basetopic) { + $this->basetopic = $basetopic; + } + + // Gets the offset for a timezone in decimal hours (e.g +01:30 returns 1.5) + public function get_time_offset($timezone) { + $dateTimeZone = new DateTimeZone($timezone); + $date = new DateTime("now", $dateTimeZone); + return $dateTimeZone->getOffset($date) / 3600; + } + + public function on($device) { + schedule_log("Tasmota $device switch on"); + + if (!isset($this->last_ctrlmode[$device])) $this->last_ctrlmode[$device] = ""; + + if ($this->last_ctrlmode[$device]!="on") { + $this->last_ctrlmode[$device] = "on"; + // user/1/tasmota/cmnd/tasmota_AABB3F/POWER -m 'OFF' + $this->mqtt_client->publish("$this->basetopic/$device/cmnd/timers","0",0); + $this->mqtt_client->publish("$this->basetopic/$device/cmnd/POWER","ON",0); + schedule_log("Tasmota $device switch on"); + } + } + + public function off($device) { + schedule_log("Tasmota $device switch off"); + + if (!isset($this->last_ctrlmode[$device])) $this->last_ctrlmode[$device] = ""; + + if ($this->last_ctrlmode[$device]!="off") { + $this->last_ctrlmode[$device] = "off"; + $this->mqtt_client->publish("$this->basetopic/$device/cmnd/timers","0",0); + $this->mqtt_client->publish("$this->basetopic/$device/cmnd/POWER","OFF",0); + schedule_log("Tasmota $device switch off"); + } + } + + + public function timer($device,$s1,$e1,$s2,$e2) { + + // TODO - doesn't work with somevalues e.g. close to midnight during BST. + schedule_log("Tasmota $device timer mode $s1 $e1 $s2 $e2"); + + $payloads = []; + $UTC = new DateTimeZone("UTC"); + $now = new DateTime("now", $UTC); + foreach ([$s1, $e1, $s2, $e2] as $i => $t) { + $dt = DateTime::createFromFormat("Hi", time_conv_dec_str($t), $UTC); + schedule_log($dt->format('H:i')); + + $action = 1; + if ($i % 2) { + $action = 0; + } + schedule_log($now->format("Y-m-d\TH:i:s\Z")." - ".$dt->format('Y-m-d\TH:i:s\Z')); + + $dt->setTimezone(new DateTimeZone(date_default_timezone_get())); // TODO should get the remote timezone rather than assume it is the same as the server + schedule_log($dt->format('H:i')); + + array_push($payloads, json_encode(array( + "Enable" => 1, + "Mode" => 0, + "Time" => $dt->format("H:i"), + "Days" => "SMTWTFS", + "Repeat" => 1, + "Action" => $action + ))); + } + + schedule_log($payloads[0]); + schedule_log($payloads[1]); + schedule_log($payloads[2]); + schedule_log($payloads[3]); + + if (!isset($this->last_ctrlmode[$device])) $this->last_ctrlmode[$device] = ""; + + if ($this->last_ctrlmode[$device]!="timer") { + $this->last_ctrlmode[$device] = "timer"; + foreach($payloads as $i => $payload) { + $this->mqtt_client->publish("$this->basetopic/$device/cmnd/Timer".($i+1), $payload, 0); + } + $this->mqtt_client->publish("$this->basetopic/$device/cmnd/Timers", 1 ,0); // Enable timers + } + + // $timer_str = time_conv_dec_str($s1)." ".time_conv_dec_str($e1)." ".time_conv_dec_str($s2)." ".time_conv_dec_str($e2); + // if (!isset($this->last_timer[$device])) $this->last_timer[$device] = ""; + + /* if ($timer_str!=$this->last_timer[$device]) { + $this->last_timer[$device] = $timer_str; + $this->mqtt_client->publish("$device/in/timer",$timer_str,0); + schedule_log("Tasmota $device set timer $timer_str"); + } */ + } + + public function send_state_request($device) { + schedule_log("Tasmota $device send state request"); + $this->mqtt_client->publish($this->basetopic."/$device/cmnd/Status8","",0); + $this->mqtt_client->publish($this->basetopic."/$device/cmnd/Timers","",0); + } + + public function handle_state_response($schedule,$message,$timezone) { + + $device = $schedule->settings->device; + + schedule_log("Tasmota $device handle_state_response $message->topic: $message->payload"); + schedule_log("$message->topic - $this->basetopic/$device/stat/STATUS0"); + if ($message->topic==$this->basetopic."/$device/stat/STATUS0") { + $p = json_decode($message->payload); + + schedule_log("stat result: $message->payload"); + + + if (isset($p->StatusNET)) { + if (isset($p->StatusNET->IPAddress)) { + $schedule->settings->ip = $p->StatusNET->IPAddress; + } + } + + if (isset($p->POWER)) { + if ($p->StatusSTS->POWER=="ON") { + $schedule->settings->ctrlmode = "on"; + schedule_log("$device is on"); + } + if ($p->StatusSTS->POWER=="OFF") { + $schedule->settings->ctrlmode = "off"; + schedule_log("$device is off"); + } + + // if ($p->POWER=="Timer" && $schedule->settings->ctrlmode!="smart") $schedule->settings->ctrlmode = "timer"; + } + + if (isset($p->Timers)) { + if ($p->Timers == "ON") { +// if ($timer->Enable == 1) { // check for mismatch on enabled, action etc. + $timeOffset = $this->get_time_offset($timezone); + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_start1 = time_conv($p->Timer1, $timeOffset); + } + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_stop1 = time_conv($p->Timer2, $timeOffset); + } + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_start2 = time_conv($p->Timer3, $timeOffset); + } + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_stop2 = time_conv($p->Timer4, $timeOffset); + } + } + } + + $schedule->runtime->last_update_from_device = time(); + return $schedule; + } else if ($message->topic==$this->basetopic."/$device/stat/RESULT") { + if (isset($p->Timers)) { + if ($p->Timers == "ON") { + // if ($timer->Enable == 1) { // check for mismatch on enabled, action etc. + $timeOffset = $this->get_time_offset($timezone); + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_start1 = time_conv($p->Timer1, $timeOffset); + } + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_stop1 = time_conv($p->Timer2, $timeOffset); + } + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_start2 = time_conv($p->Timer3, $timeOffset); + } + if ($p->Timer1->Enable == 1) { + $schedule->settings->timer_stop2 = time_conv($p->Timer4, $timeOffset); + } + } + } + + $schedule->runtime->last_update_from_device = time(); + return $schedule; + } + return false; + } + + public function get_state($mqtt_request,$device,$timezone) { + + schedule_log("Tasmota $device switch on"); + + $result = json_decode($mqtt_request->request($this->basetopic."/$device/cmnd/Status0","",$this->basetopic."/$device/stat/STATUS0")); + if (!$result) { + schedule_log("No result fetching state for $device"); + return false; + } + /* $timers_result = json_decode($mqtt_request->request($this->basetopic."/$device/cmnd/Timers","",$this->basetopic."/$device/stat/RESULT")); + if (!$result) { + schedule_log("No result fetching timers for $device"); + return false; + } + $timezone_result = json_decode($mqtt_request->request($this->basetopic."/$device/cmnd/Timezone","",$this->basetopic."/$device/stat/RESULT")); + if (!$timezone_result) { + schedule_log("No result fetching timers for $device"); + return false; + } + $timezone_result->Timezone; */ + + $state = new stdClass; + if ($result->StatusSTS->POWER == "ON") { + $state->ctrl_mode = "on"; + } else if ($result->StatusSTS->POWER == "OFF") { + $state->ctrl_mode = "off"; + } + + + /* $timer_parts = explode(" ",$result->timer); + + $dateTimeZone = new DateTimeZone($timezone); + $date = new DateTime("now", $dateTimeZone); + $timeOffset = $dateTimeZone->getOffset($date) / 3600; + */ + + /* + "Timer1": { + "Enable": 1, + "Mode": 0, + "Time": "03:00", + "Window": 0, + "Days": "1111111", + "Repeat": 1, + "Output": 1, + "Action": 1 + }, + */ + + + + + $state->timer_start1 = conv_time($timer_parts[0],$timeOffset); + $state->timer_stop1 = conv_time($timer_parts[1],$timeOffset); + $state->timer_start2 = conv_time($timer_parts[2],$timeOffset); + $state->timer_stop2 = conv_time($timer_parts[3],$timeOffset); +// $state->voltage_output = $result->vout*1;*/ + return $state; + } + + public function auto_update_timeleft($schedule) { + return $schedule; + } +} diff --git a/lib/misc.php b/lib/misc.php index 4fdc151..4a84ab0 100644 --- a/lib/misc.php +++ b/lib/misc.php @@ -7,6 +7,7 @@ function time_offset($t,$timeOffset) { return $t; } +// time_conv and conv_time??? function time_conv($t,$timeOffset){ $t = floor($t*0.01) + ($t*0.01 - floor($t*0.01))/0.6; $t += $timeOffset; @@ -24,6 +25,8 @@ function conv_time($time,$timeOffset) { return $t; } + +// Converts fractional time to human readable time, e.g. 14.5 to "14$div30" function time_conv_dec_str($t,$div="") { $h = floor($t); $m = round(($t-$h)*60); From 613aa33b25fe622cc2d025438c8facdcf180acf5 Mon Sep 17 00:00:00 2001 From: Luke Farrar Date: Wed, 15 Sep 2021 15:56:52 +0100 Subject: [PATCH 08/10] Use $settings not $system for MQTT host/port --- demandshaper-module/Views/add_device.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demandshaper-module/Views/add_device.php b/demandshaper-module/Views/add_device.php index 041f658..a6f8c4f 100644 --- a/demandshaper-module/Views/add_device.php +++ b/demandshaper-module/Views/add_device.php @@ -11,7 +11,7 @@ */ $v=2; -global $path, $mysqli, $redis, $user, $session, $system; // TODO system doesn't import +global $path, $mysqli, $redis, $user, $session, $settings; $emoncmspath = $path; if ($remoteaccess) $emoncmspath .= "remoteaccess/"; // if ($remoteaccess) $emoncmspath .= "remoteaccess/"; @@ -72,8 +72,8 @@

3. In the Tasmota device configuration select Configuration > Configure MQTT.

Set:

    -
  • Host:
  • -
  • Port:
  • +
  • Host:
  • +
  • Port:
  • User: username ?>
  • Password (API write key): apikey_write ?>
  • Topic: tasmota_%06X
  • From 621ef05c2e7f1df0b63d25feb157305d34203ed8 Mon Sep 17 00:00:00 2001 From: Luke Farrar Date: Wed, 22 Sep 2021 19:15:26 +0100 Subject: [PATCH 09/10] Improve instructions. Slightly. --- demandshaper-module/Views/add_device.php | 32 +++++++++++++----------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/demandshaper-module/Views/add_device.php b/demandshaper-module/Views/add_device.php index a6f8c4f..5405b37 100644 --- a/demandshaper-module/Views/add_device.php +++ b/demandshaper-module/Views/add_device.php @@ -65,21 +65,25 @@

    4. With the smartplug WIFI settings configured, connect back to you home network and keep this window open. After a couple of minutes a notice will appear asking whether to allow device at the given ip address to connect. Click allow and wait a couple of minutes for the smart plug to appear in the left hand menu. Click on the smart plug to start scheduling it.

-
Tasmota Smart Plug
+
Tasmota Device
-

1. Commission your device as per the Tasmota instructions.

-

2. In the Tasmota device configuration select Configuration > Configure Other and set MQTT enabled.

-

3. In the Tasmota device configuration select Configuration > Configure MQTT.

-

Set: -

    -
  • Host:
  • -
  • Port:
  • -
  • User: username ?>
  • -
  • Password (API write key): apikey_write ?>
  • -
  • Topic: tasmota_%06X
  • -
  • Full topic: user/id?>/%topic%/%prefix%
  • -
-

+
    +
  1. Commission your device as per the Tasmota instructions.

  2. +
  3. Navigate to your Tasmota device configuration in your webbrowser.

  4. +
  5. Select Configuration > Configure Other and set MQTT enabled. Save, and return to Configuration.

  6. +
  7. +

    Select Configure MQTT. Enter (or copy and paste) the values below before saving:

    +
      +
    • Host:
    • +
    • Port:
    • +
    • User: username ?>
    • +
    • Password (API write key): apikey_write ?>
    • +
    • Topic: tasmota_%06X
    • +
    • Full topic: user/id?>/%topic%/%prefix%
    • +
    +
  8. +
  9. A new Tasmota entry should appear in the devices menu shortly.
  10. +
OpenEVSE Charging Station
From dab57cd56d5566796b8bb44a9ca417e5c03298b6 Mon Sep 17 00:00:00 2001 From: Luke Farrar Date: Wed, 22 Sep 2021 20:05:36 +0100 Subject: [PATCH 10/10] Still issues with state changes - possibly generic, more likely not setting state as timer on get_state --- devices/tasmota.php | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/devices/tasmota.php b/devices/tasmota.php index c021320..5dc02ef 100644 --- a/devices/tasmota.php +++ b/devices/tasmota.php @@ -39,7 +39,7 @@ public function on($device) { // user/1/tasmota/cmnd/tasmota_AABB3F/POWER -m 'OFF' $this->mqtt_client->publish("$this->basetopic/$device/cmnd/timers","0",0); $this->mqtt_client->publish("$this->basetopic/$device/cmnd/POWER","ON",0); - schedule_log("Tasmota $device switch on"); + schedule_log("Tasmota $device switch on sent"); } } @@ -52,7 +52,7 @@ public function off($device) { $this->last_ctrlmode[$device] = "off"; $this->mqtt_client->publish("$this->basetopic/$device/cmnd/timers","0",0); $this->mqtt_client->publish("$this->basetopic/$device/cmnd/POWER","OFF",0); - schedule_log("Tasmota $device switch off"); + schedule_log("Tasmota $device switch off sent"); } } @@ -65,7 +65,7 @@ public function timer($device,$s1,$e1,$s2,$e2) { $payloads = []; $UTC = new DateTimeZone("UTC"); $now = new DateTime("now", $UTC); - foreach ([$s1, $e1, $s2, $e2] as $i => $t) { + foreach ([$s1, $e1] as $i => $t) { // Only one time slot is currently in use - , $s2, $e2 $dt = DateTime::createFromFormat("Hi", time_conv_dec_str($t), $UTC); schedule_log($dt->format('H:i')); @@ -78,39 +78,38 @@ public function timer($device,$s1,$e1,$s2,$e2) { $dt->setTimezone(new DateTimeZone(date_default_timezone_get())); // TODO should get the remote timezone rather than assume it is the same as the server schedule_log($dt->format('H:i')); - array_push($payloads, json_encode(array( + $payload = json_encode(array( "Enable" => 1, "Mode" => 0, "Time" => $dt->format("H:i"), "Days" => "SMTWTFS", "Repeat" => 1, "Action" => $action - ))); + )); + array_push($payloads, $payload); + schedule_log($payload); } - schedule_log($payloads[0]); schedule_log($payloads[1]); schedule_log($payloads[2]); schedule_log($payloads[3]); if (!isset($this->last_ctrlmode[$device])) $this->last_ctrlmode[$device] = ""; + if (!isset($this->last_timer[$device])) $this->last_timer[$device] = ""; - if ($this->last_ctrlmode[$device]!="timer") { + + $timer_str = time_conv_dec_str($s1)." ".time_conv_dec_str($e1)." ".time_conv_dec_str($s2)." ".time_conv_dec_str($e2); + + // If state or time has changed + if ($this->last_ctrlmode[$device]!="timer" || $timer_str!=$this->last_timer[$device]) { $this->last_ctrlmode[$device] = "timer"; + $this->last_timer[$device] = $timer_str; foreach($payloads as $i => $payload) { $this->mqtt_client->publish("$this->basetopic/$device/cmnd/Timer".($i+1), $payload, 0); } $this->mqtt_client->publish("$this->basetopic/$device/cmnd/Timers", 1 ,0); // Enable timers + schedule_log("Tasmota $device set timer $timer_str"); } - - // $timer_str = time_conv_dec_str($s1)." ".time_conv_dec_str($e1)." ".time_conv_dec_str($s2)." ".time_conv_dec_str($e2); - // if (!isset($this->last_timer[$device])) $this->last_timer[$device] = ""; - - /* if ($timer_str!=$this->last_timer[$device]) { - $this->last_timer[$device] = $timer_str; - $this->mqtt_client->publish("$device/in/timer",$timer_str,0); - schedule_log("Tasmota $device set timer $timer_str"); - } */ } public function send_state_request($device) {