2626DATE_STR_FORMAT = "%Y-%m-%d"
2727DATE_TIME_STR_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
2828
29+ # Hard-wired smart meter day/night rate times
30+ OCTOPUS_NIGHT_RATE_START_HOUR = 0
31+ OCTOPUS_NIGHT_RATE_START_MINUTE = 30
32+ OCTOPUS_NIGHT_RATE_END_HOUR = 7
33+ OCTOPUS_NIGHT_RATE_END_MINUTE = 30
34+
35+ OCTOPUS_DAY_RATE_START_HOUR = 7
36+ OCTOPUS_DAY_RATE_START_MINUTE = 30
37+ OCTOPUS_DAY_RATE_END_HOUR = 0
38+ OCTOPUS_DAY_RATE_END_MINUTE = 30
39+
2940
3041def is_active (now_utc , activeFrom , activeTo ):
3142 if not activeFrom :
@@ -673,6 +684,50 @@ async def async_get_saving_sessions(self, account_id):
673684 else :
674685 return response_data .get ("savingSessions" , {})
675686
687+ async def async_get_day_night_rates (self , url ):
688+ """
689+ Get day and night rates from Octopus
690+ """
691+ mdata = []
692+ self .log ("Info: Octopus tariff has day and night rates, fetching both" )
693+ url_day = url .replace ("standard-unit-rates" , "day-unit-rates" )
694+ url_night = url .replace ("standard-unit-rates" , "night-unit-rates" )
695+ result_day = await self .fetch_url_cached (url_day )
696+ result_night = await self .fetch_url_cached (url_night )
697+ # is_night_rate = self.__is_between_times(rate, "00:30:00", "07:30:00", True)
698+ # Find the current day rate by scanning all the values looking at valid from date and picking the latest that is before now
699+ current_day_rate = None
700+ current_night_rate = None
701+ for rate in result_day :
702+ valid_from_stamp = rate .get ("valid_from" , "" )
703+ # Convert from string to datetime
704+ valid_from_stamp = datetime .strptime (valid_from_stamp , DATE_TIME_STR_FORMAT )
705+ if valid_from_stamp <= self .now_utc :
706+ current_day_rate = rate .get ("value_inc_vat" , None )
707+ for rate in result_night :
708+ valid_from_stamp = rate .get ("valid_from" , "" )
709+ # Convert from string to datetime
710+ valid_from_stamp = datetime .strptime (valid_from_stamp , DATE_TIME_STR_FORMAT )
711+ if valid_from_stamp <= self .now_utc :
712+ current_night_rate = rate .get ("value_inc_vat" , None )
713+ self .log ("Info: Current day rate {} night rate {}" .format (current_day_rate , current_night_rate ))
714+ if current_day_rate is not None and current_night_rate is not None :
715+ # Now create a combined list of rates, start from 2 days back and go forward 3 days with the day and night rates
716+ night_start_time = self .now_utc .replace (hour = OCTOPUS_NIGHT_RATE_START_HOUR , minute = OCTOPUS_NIGHT_RATE_START_MINUTE , second = 0 , microsecond = 0 ) - timedelta (days = 2 )
717+ night_end_time = night_start_time .replace (hour = OCTOPUS_NIGHT_RATE_END_HOUR , minute = OCTOPUS_NIGHT_RATE_END_MINUTE )
718+ day_start_time = night_start_time .replace (hour = OCTOPUS_DAY_RATE_START_HOUR , minute = OCTOPUS_DAY_RATE_START_MINUTE )
719+ day_end_time = night_start_time .replace (hour = OCTOPUS_DAY_RATE_END_HOUR , minute = OCTOPUS_DAY_RATE_END_MINUTE ) + timedelta (days = 1 )
720+ for day in range (8 ):
721+ # Night rate
722+ mdata .append ({"valid_from" : night_start_time .strftime (DATE_TIME_STR_FORMAT ), "valid_to" : night_end_time .strftime (DATE_TIME_STR_FORMAT ), "value_inc_vat" : current_night_rate })
723+ # Day rate
724+ mdata .append ({"valid_from" : day_start_time .strftime (DATE_TIME_STR_FORMAT ), "valid_to" : day_end_time .strftime (DATE_TIME_STR_FORMAT ), "value_inc_vat" : current_day_rate })
725+ night_start_time += timedelta (days = 1 )
726+ night_end_time += timedelta (days = 1 )
727+ day_start_time += timedelta (days = 1 )
728+ day_end_time += timedelta (days = 1 )
729+ return mdata
730+
676731 async def async_download_octopus_url (self , url ):
677732 """
678733 Download octopus rates directly from a URL
@@ -683,7 +738,7 @@ async def async_download_octopus_url(self, url):
683738 while url and pages < 3 :
684739 self .requests_total += 1
685740 r = requests .get (url )
686- if r .status_code not in [200 , 201 ]:
741+ if r .status_code not in [200 , 201 , 400 ]:
687742 self .failures_total += 1
688743 self .log ("Warn: Error downloading Octopus data from URL {}, code {}" .format (url , r .status_code ))
689744 return {}
@@ -694,9 +749,28 @@ async def async_download_octopus_url(self, url):
694749 self .failures_total += 1
695750 self .log ("Warn: Error downloading Octopus data from URL {} (JSONDecodeError)" .format (url ))
696751 return {}
752+
753+ if r .status_code == 400 :
754+ detail = data .get ("detail" , "" )
755+ if "This tariff has day and night rates" in detail :
756+ self .log ("Info: Octopus tariff has day and night rates, fetching both" )
757+ mdata = await self .async_get_day_night_rates (url )
758+ if mdata :
759+ return mdata
760+ else :
761+ self .failures_total += 1
762+ self .log ("Warn: Error downloading Octopus data from URL {} (No Results)" .format (url ))
763+ return {}
764+ else :
765+ self .failures_total += 1
766+ self .log ("Warn: Error downloading Octopus data from URL {} (400) - {}" .format (url , detail ))
767+ return {}
768+
697769 if "results" in data :
698770 mdata += data ["results" ]
699771 else :
772+ detail = data .get ("detail" , "" )
773+
700774 self .failures_total += 1
701775 self .log ("Warn: Error downloading Octopus data from URL {} (No Results)" .format (url ))
702776 return {}
0 commit comments