Timezone conversion for the Time Input widget

Yours.

You were right, I do that much with Droid4X these days I had forgotten to upgrade to version 15.2 on my actual phone.

Can you confirm that there are currently no widgets that allow users to paste text into a “field”.

Just terminal If I understood you correctly.

I thought I tried in Terminal but I’ll try again.

@Dmitriy I tried to paste to Terminal on my Samsung S3 and the only option I have is to enter text from the pop up keyboard. Paste works in other apps.

I have tried this example. When I change the time zone the t.getStartHour and t.getStopHour is always UTC time.
nowseconds = ((hour() * 3600) + (minute() * 60) + second()); this is the correct time zone.
How do I get the two in the same time zone?

Checked schedule at: 19:31:12
Schedule ACTIVE today
Start: 0:35
Stop : 1:30
Time zone: America/New_York
Time zone offset: 4294949296
Day 1 is selected
Motor STARTED at 0:35
Motor STOPPED at 1:30

Checked schedule at: 19:31:21
Schedule INACTIVE today

Checked schedule at: 19:31:30
Schedule ACTIVE today
Start: 0:35
Stop : 1:30
Time zone: America/New_York
Time zone offset: 4294949296
Day 1 is selected
Day 2 is selected
Day 3 is selected
Day 4 is selected
Day 5 is selected
Day 6 is selected
Day 7 is selected
Motor STARTED at 0:35
Motor STOPPED at 1:30

[670256] Time sync: OK
Checked schedule at: 19:29:13
Schedule ACTIVE today
Start: 0:20
Stop : 1:30
Time zone: GMT
Time zone offset: 0
Day 1 is selected
Motor STARTED at 0:20
Motor STOPPED at 1:30

Checked schedule at: 19:30:12
Schedule ACTIVE today
Start: 0:20
Stop : 1:30
Time zone: GMT
Time zone offset: 0
Day 1 is selected
Motor STARTED at 0:20
Motor STOPPED at 1:30

@BSlaght do you have the RTC widget in your Project?

Perhaps post your sketch and your project. Maybe the sketch we posted doesn’t like AM / PM used in America and for test purposes you could try to use the 24 hour clock to see if that helps.

I have reached the max the number of pictures I can upload upload.
I am using the iPhone App
I have RTC on V0
Time Input to V1

Changed to the 24 hour clock.
Set start to 15:56
Set Stop to 15:59

Output:
[240] Connecting to XXX
[1241] Connected to WiFi
[1241] IP: 192.168.1.100
[1241]
___ __ __
/ _ )/ /_ _____ / /__
/ _ / / // / _ / '/
/
//_, /////_
/
__/ v0.4.0 on NodeMCU

[5001] Connecting to blynk-cloud.com:8442
[5079] Ready (ping: 1ms).
[10080] Connecting to blynk-cloud.com:8442
[10157] Ready (ping: 0ms).
[10212] Time sync: OK

Checked schedule at: 15:58:38
Schedule ACTIVE today
Start: 20:56
Stop : 20:59
Time zone: America/New_York
Time zone offset: 4294949296
Day 2 is selected
Motor NOT STARTED today

/**************************************************************
 * timeinput.ino Demonstrate interaction of Time library with
 * Blynk's TimeInput widget, Costas 14/9/16
 * App project setup:
 * RTC on V0 and Time Input widget on V1.
 *
 **************************************************************/

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <SimpleTimer.h>
#include <TimeLib.h>
#include <WidgetRTC.h>

SimpleTimer timer;

WidgetRTC rtc;

BLYNK_ATTACH_WIDGET(rtc, V0);
// #define server "blynk.cloud-com"   // or "blynk.cloud-com" for Blynk's cloud server
#define TestLED 2                 // on board LED pin assignment
char Date[16];
char Time[16];
char auth[] = "xxx";
char ssid[] = "xxx";
char pass[] = "xxx";
long startsecondswd;            // weekday start time in seconds
long stopsecondswd;             // weekday stop  time in seconds
long nowseconds;                // time now in seconds

void setup()
{
  pinMode(TestLED, OUTPUT);
  digitalWrite(TestLED, HIGH); // set LED OFF
  Serial.begin(115200);
  Serial.println("\Starting");
  Blynk.begin(auth, ssid, pass);
  int mytimeout = millis() / 1000;
  while (Blynk.connect() == false) { // try to connect to server for 10 seconds
    if((millis() / 1000) > mytimeout + 8){ // try local server if not connected within 9 seconds
       break;
    }
  }
  rtc.begin();
  timer.setInterval(60000L, activetoday);  // check every minute if schedule should run today 
  timer.setInterval(30000L, reconnectBlynk);  // check every 30s if still connected to server 
}

void activetoday(){        // check if schedule should run today
  if(year() != 1970){
    Blynk.syncVirtual(V1); // sync timeinput widget    
  }
}

BLYNK_WRITE(V1) {
  sprintf(Date, "%02d/%02d/%04d",  day(), month(), year());
  sprintf(Time, "%02d:%02d:%02d", hour(), minute(), second());
  
  TimeInputParam t(param);
  
  Serial.print("Checked schedule at: ");
  Serial.println(Time);
  int dayadjustment = -1;  
  if(weekday() == 1){
    dayadjustment =  6; // needed for Sunday, Time library is day 1 and Blynk is day 7
  }
  if(t.isWeekdaySelected((weekday() + dayadjustment))){ //Time library starts week on Sunday, Blynk on Monday
    Serial.println("Schedule ACTIVE today");
    if (t.hasStartTime()) // Process start time
    {
      Serial.println(String("Start: ") + t.getStartHour() + ":" + t.getStartMinute());
    }
    if (t.hasStopTime()) // Process stop time
    {
      Serial.println(String("Stop : ") + t.getStopHour() + ":" + t.getStopMinute());
    }
    // Display timezone details, for information purposes only 
    Serial.println(String("Time zone: ") + t.getTZ()); // Timezone is already added to start/stop time 
    Serial.println(String("Time zone offset: ") + t.getTZ_Offset()); // Get timezone offset (in seconds)
  
    for (int i = 1; i <= 7; i++) {  // Process weekdays (1. Mon, 2. Tue, 3. Wed, ...)
      if (t.isWeekdaySelected(i)) {
        Serial.println(String("Day ") + i + " is selected");
      }
    } 
    nowseconds = ((hour() * 3600) + (minute() * 60) + second());
    startsecondswd = (t.getStartHour() * 3600) + (t.getStartMinute() * 60);
    //Serial.println(startsecondswd);  // used for debugging
    if(nowseconds >= startsecondswd){    
      Serial.print("Motor STARTED at");
      Serial.println(String(" ") + t.getStartHour() + ":" + t.getStartMinute());
      if(nowseconds <= startsecondswd + 90){    // 90s on 60s timer ensures 1 trigger command is sent
        digitalWrite(TestLED, LOW); // set LED ON
        // code here to switch the relay ON
      }      
    }
    else{
      Serial.println("Motor NOT STARTED today");
      // nothing more to do here, waiting for motor to be turned on later today      
    }
    stopsecondswd = (t.getStopHour() * 3600) + (t.getStopMinute() * 60);
    //Serial.println(stopsecondswd);  // used for debugging
    if(nowseconds >= stopsecondswd){
      Serial.print("Motor STOPPED at");
      Serial.println(String(" ") + t.getStopHour() + ":" + t.getStopMinute());
      if(nowseconds <= stopsecondswd + 90){   // 90s on 60s timer ensures 1 trigger command is sent
        digitalWrite(TestLED, HIGH); // set LED OFF
        // code here to switch the relay OFF
      }              
    }
    else{
      if(nowseconds >= startsecondswd){  // only show if motor has already started today
        Serial.println("Motor is still RUNNING");
        // nothing more to do here, waiting for motor to be turned off later today
      }          
    }
  }
  else{
    Serial.println("Schedule INACTIVE today");
    // nothing to do today, check again in 1 minutes time    
  }
  Serial.println();
}

void reconnectBlynk() {
  if (!Blynk.connected()) {
    if(Blynk.connect()) {
      BLYNK_LOG("Reconnected");
    } else {
      BLYNK_LOG("Not reconnected");
    }
  }
}

void loop()
{
  if (Blynk.connected()) {
    Blynk.run();
  }
  timer.run();
}
1 Like

@BSlaght I suspect it might be an issue with iOS as it’s not as developed as it is for Android.

@Dmitriy are there some aspects of TimeInput missing from iOS?

@BSlaght I changed my Android time settings to AM / PM from 24 hour clock and Time Input seems to work fine.

I notice your screenshot shows hours, minutes and seconds for the Start and Stop times. This to me suggests iOS isn’t using the correct implementation of Time Input as it was decided that seconds are not important for the widget and they were removed when the Android version was finalised.

Can’t answer right now. Need to have a look.

I’m not following this topic since it started, but what I see here is some weird value as a result of called getTZ_Offset(). In your example it is 4294949296 but should be: -5*3600 = -18000. I’m not sure if this is the result of conversion to String object (as it is done in Println() - is it correct way?) or problem with passing value from app to “blynked” hardware. Compare value printed in web viewer after sending:
http://blynk-cloud.com/xxxxxblynkxxxtokenxxxxxxxx/get/V1
where V1 is yours Time input widget virtual pin, I think.
The output should look like this:
["28800�47160�Poland�4,5�3600"]
where first and second value is StartTime and StopTime respectively, and the last is TZ offset (in Poland now +1hour - unfortunate winter time).

By the way: You don’t need to calculate Start time and Stop Time in your sketch, as these are send directly from Blynk server, and can be read by calling:
long startsecondswd = param[0].asDouble();
long stopsecondswd = param[1].asDouble();
respectively (I’ve changed asInt() to asDouble() as Int may overflow in some cases - though not in Your case)

The second from the end (here 4,5) are the days selected, for clarification.

1 Like

@marvin7 some useful info there, thanks, especially param[0].asDouble() etc.

For me the API call gives:

[“43740�43800�Asia/Nicosia�1,2,3,4,5,6,7�7200”]

@BSlaght what does the API call give you?

@Costas: glad, can help. I’ve been through it recently. My biggest problem was to handle the situation, where (read carefully :wink: ): StartTime is LATER in the day than StopTime (for working overnight), while handling correctly working days (StartTime occurs on SELECTED day). Now the only situation I’m not able to solve correctly is, when StartTime is LATER then StopTime, the StartTime already passed and the day has changed to day which is DISABLED in Timer, and THEN the device starts/restarts (POWER ON). It should turn itself ON, but this situation is not handled in software. But that is for different topic, I think. If someone is interested, I’m ready to share, perhaps we will be able to solve this problem.

@marvin7 I don’t think the rolling over of days has to be a new topic as it’s all related to the Time Input widget.

In fact the OP and I discussed this issue and similar issues such as no start or no stop time via PM.

I think one way to cover some of the issues is simply to use more than one Time Input widget.
Another way is to set a flag in EEPROM (RTC memory and general Filesystem of ESP’s) which can be checked on reboot to see if you still have a schedule running from the previous day.

Yes, but you still have to SET this flag. And what if the device is STILL powered off ? Off course this can be handled, but for now (as I was in a “hurry”) I left this specific situation. The rest handles very well - no problem for few weeks.
So, here is my (fragment) of code responsible for handling TIMER. The RTC is kept by NTP client, and here is omitted, for clarity:

First TimerSettings virtual pin input handler:

TimerBitMask variable explanation:
BlynkTimer_en is set when TIMER-button is selected in APP
BlynkTimer_on is set, when TIMER is ACTIVE (i.e it has to switch sth on/off as selected by widget)
BlynkTimer_set is set, when all neccesary values are received from APP/widget

//these are globals, among others off course
uint8_t TimerBitMask = 0; //Bit mask (bit 0 = BlynkTimer_en, bit 1 = BlynkTimer_on, 2 = BlynkTimer_set
#define BlynkTimer_en 0
#define BlynkTimer_on 1
#define BlynkTimer_set 2

uint8_t weekdaySelected
...

BLYNK_WRITE(V8) {  // TIMER settings
	TimeInputParam t(param);
	if (timeStatus() == timeSet) {              //checks if correct RTC is set
		bitSet(TimerBitMask, BlynkTimer_set);
		Serial.println();
		Serial.println(F(" * TIMER SETTINGS STORED IN BLYNK:"));
		if (t.hasStartTime())
		{
			Serial.print (F("Start time: "));
			Serial.printf(timeFormatString, t.getStartHour(), t.getStartMinute(), t.getStartSecond());
			Serial.println();
			BlynkTimer_start = param[0].asInt();
		}
		else if (t.isStartSunrise())
		{
			Serial.println(F("Start at sunrise is SET (however not  supported currently!)"));
		}
		else if (t.isStartSunset())
		{
			Serial.println(F("Start at sunset is SET (however not  supported currently!)"));
		}
		else
		{
			Serial.println(F("NO Start event is SET"));
			bitClear(TimerBitMask, BlynkTimer_set);
		}

		if (t.hasStopTime())
		{
			Serial.print (F("Stop time: "));
			Serial.printf(timeFormatString, t.getStopHour(), t.getStopMinute(), t.getStopSecond());
			Serial.println();
			BlynkTimer_stop = param[1].asInt();
		}
		else if (t.isStopSunrise())
		{
			Serial.println(F("Stop at sunrise is SET (however not  supported currently!)"));
		}
		else if (t.isStopSunset())
		{
			Serial.println(F("Stop at sunset is SET (however not  supported currently!)"));
		}
		else
		{
			Serial.println(F("NO Stop event is SET"));
			bitClear(TimerBitMask, BlynkTimer_set);
		}

		Serial.println(String(F("Time zone: ")) + t.getTZ());

		TimeZone = (int)t.getTZ_Offset() / 3600;
		Serial.printf("TZ offset: %+03d", TimeZone );
		Serial.println();

		// Process weekdays ( global variable [weekdaySelected] is bit-masked, where bit 0= Mon, bit 6= Sun. Bit 7 not used and should be left at 0)
		Serial.print(F("Timer START allowed at: "));
		weekdaySelected = 0;
		for (uint8_t i = 0; i < 7; i++) {
			if (t.isWeekdaySelected(i + 1)) {
				Serial.print(weekdayName[i]);
				Serial.print(',');
				bitSet(weekdaySelected, i);
			}
		}
		adjustTime (TimeZone * 3600);
	}
	Serial.print(TwoLineBreak);
	Serial.print(F("Timer operation is: "));
	if (bitRead(TimerBitMask, BlynkTimer_set)) Serial.println (F("ALLOWED")); else Serial.println (F("NOT ALLOWED due to invalid setup"));
}

And now the Timer handling function (actually procedure - it returns nothing) where “the magic happens” :stuck_out_tongue: :

void checkTimer() {
	if (bitRead(TimerBitMask, BlynkTimer_set) && bitRead(TimerBitMask, BlynkTimer_en)) {
		uint32_t nowTime = hour() * 3600 + minute() * 60;
		if (BlynkTimer_start < BlynkTimer_stop) {
			if (bitRead(weekdaySelected, weekday() - 1) && nowTime >= BlynkTimer_start && nowTime < BlynkTimer_stop) bitSet(TimerBitMask, BlynkTimer_on);
			else bitClear(TimerBitMask, BlynkTimer_on);
		}
		else if (BlynkTimer_start > BlynkTimer_stop) {
			if (nowTime >= BlynkTimer_stop && nowTime < BlynkTimer_start) bitClear(TimerBitMask, BlynkTimer_on);
			else if (bitRead(weekdaySelected, weekday() - 1) &&  nowTime >= BlynkTimer_start) bitSet(TimerBitMask, BlynkTimer_on);
		}
	}
	else bitSet(TimerBitMask, BlynkTimer_on);
} 

The checkTimer() is called periodically from loop() where it decides (by checking bit _on) if the output pin should be SET or CLEAR. There are other variables as well, and as a whole it looks like this:

checkTimer();
			if (Tc_in - Tc_set >= 0 || heat_en == 0 || !bitRead(TimerBitMask, BlynkTimer_on)) {
				heat_on = 0;
			}
			else if (Tc_set - Tc_in >= T_HYSTERESIS && heat_en == 1 && bitRead(TimerBitMask, BlynkTimer_on)) {
				heat_on = 1;
			}
			if ((!digitalRead (heater_relay)) && heat_on) {
				digitalWrite (heater_relay, heat_on);
				heat_timer.attach(10, heat_timer_counter);
			}
			else if (digitalRead(heater_relay) && (!heat_on)) {
				digitalWrite (heater_relay, heat_on);
				heat_timer.detach();
			}

Hope this helps, and waiting for comments :wink:

Yeap, now I’ve found a bug :slight_smile: in Timer virtual pin handler - that is because Sunrise/sunset IS NOT supported, but bit _set is NOT CLEARED (though it should be). But this only occurs (i think) if someone will set in APP. Will care later.

UPDATE: After reading the topic from the beginning, I just realized, that my code IS NOT compatible with default Arduino Time Library!! For compatibility and easy of use, I modified the Time library, so now the day of week in RTC (supported by Time Library and NTP) is the same as in Blynk TimeInput widget. . EVERYONE analyzing my code should keep it in mind!

Can you get the time zone offset in hours from this “Time zone offset: 4294949296” mathematically, if so How? The answer I am looking for is -5 per my example above.