We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AUS-TAS-KI reports negative wind generation sometimes
Small values, so probably self-consumption. I'll prepare a fix

[Kibana](https://kibana.electricitymap.org/app/kibana#/discover/10af54f0-0c4a-11e9-85c1-1d63df8c862c?_g=(refreshInterval:('$$hashKey':'object:232',display:'5%20minutes',pause:!f,section:2,value:300000),time:(from:now-24h,mode:quick,to:now))&_a=(columns:!(message,extra.key,level),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:level,negate:!f,params:(query:ERROR,type:phrase),type:phrase,value:ERROR),query:(match:(level:(query:ERROR,type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:extra.key,negate:!f,params:(query:AUS-TAS-KI,type:phrase),type:phrase,value:AUS-TAS-KI),query:(match:(extra.key:(query:AUS-TAS-KI,type:phrase))))),index:'96f67170-0c49-11e9-85c1-1d63df8c862c',interval:auto,query:(language:lucene,query:''),sort:!('@timestamp',desc))):

>November 17th 2020, 17:23:16.743
invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 23, 16, 425060, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.338, 'solar': 0.0, 'wind': -0.003, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.003
AUS-TAS-KI
ERROR
>November 17th 2020, 17:02:28.794invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 2, 27, 648934, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.217, 'solar': 0.0, 'wind': -0.006, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.006
AUS-TAS-KI
ERROR
>November 17th 2020, 16:49:52.790invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 2, 49, 49, 351083, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.264, 'solar': 0.0, 'wind': -0.017, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.017
AUS-TAS-KI
ERROR
AUS-TAS-KI reports negative wind generation sometimes
Small values, so probably self-consumption. I'll prepare a fix

[Kibana](https://kibana.electricitymap.org/app/kibana#/discover/10af54f0-0c4a-11e9-85c1-1d63df8c862c?_g=(refreshInterval:('$$hashKey':'object:232',display:'5%20minutes',pause:!f,section:2,value:300000),time:(from:now-24h,mode:quick,to:now))&_a=(columns:!(message,extra.key,level),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:level,negate:!f,params:(query:ERROR,type:phrase),type:phrase,value:ERROR),query:(match:(level:(query:ERROR,type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:extra.key,negate:!f,params:(query:AUS-TAS-KI,type:phrase),type:phrase,value:AUS-TAS-KI),query:(match:(extra.key:(query:AUS-TAS-KI,type:phrase))))),index:'96f67170-0c49-11e9-85c1-1d63df8c862c',interval:auto,query:(language:lucene,query:''),sort:!('@timestamp',desc))):

>November 17th 2020, 17:23:16.743
invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 23, 16, 425060, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.338, 'solar': 0.0, 'wind': -0.003, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.003
AUS-TAS-KI
ERROR
>November 17th 2020, 17:02:28.794invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 2, 27, 648934, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.217, 'solar': 0.0, 'wind': -0.006, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.006
AUS-TAS-KI
ERROR
>November 17th 2020, 16:49:52.790invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 2, 49, 49, 351083, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.264, 'solar': 0.0, 'wind': -0.017, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.017
AUS-TAS-KI
ERROR

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsers/AUS_TAS_KI.py`
Content:
```
1 # Initial PR https://github.com/tmrowco/electricitymap-contrib/pull/2456
2 # Discussion thread https://github.com/tmrowco/electricitymap-contrib/issues/636
3 # A promotion webpage for King's Island energy production is here : https://www.hydro.com.au/clean-energy/hybrid-energy-solutions/success-stories/king-island
4 # As of 09/2020, it embeds with <iframe> the URI https://data.ajenti.com.au/KIREIP/index.html
5 # About the data, the feed we get seems to be counters with a 2 seconds interval.
6 # That means that if we fetch these counters every 15 minutes, we only are reading "instantaneous" metters that could differ from the total quantity of energies at play. To get the very exact data, we would need to have a parser running constanty to collect those 2-sec interval counters.
7 
8 import asyncio
9 import json
10 import logging
11 import arrow
12 from signalr import Connection
13 from requests import Session
14 
15 class SignalR:
16     def __init__(self, url):
17         self.url = url
18     
19     def update_res(self, msg):
20         if (msg != {}):
21             self.res = msg
22 
23     def get_value(self, hub, method):
24         self.res = {}
25         with Session() as session:
26             #create a connection
27             connection = Connection(self.url, session)
28             chat = connection.register_hub(hub)
29             chat.client.on(method, self.update_res)
30             connection.start()
31             connection.wait(3)
32             connection.close()
33             return self.res
34         
35 def parse_payload(logger, payload):
36     technologies_parsed = {}
37     logger.debug(f"serie : {json.dumps(payload)}")
38     for technology in payload['technologies']:
39         assert technology['unit'] == 'kW'
40         # The upstream API gives us kW, we need MW
41         technologies_parsed[technology['id']] = int(technology['value'])/1000
42     logger.debug(f"production : {json.dumps(technologies_parsed)}")
43 
44     biodiesel_percent = payload['biodiesel']['percent']
45 
46     return technologies_parsed, biodiesel_percent
47 
48 # Both keys battery and flywheel are negative when storing energy, and positive when feeding energy to the grid
49 def format_storage_techs(technologies_parsed):
50     storage_techs = technologies_parsed['battery']+technologies_parsed['flywheel']
51     battery_production = storage_techs if storage_techs > 0 else 0
52     battery_storage = storage_techs if storage_techs < 0 else 0
53 
54     return battery_production, battery_storage
55 
56 def fetch_production(zone_key='AUS-TAS-KI', session=None, target_datetime=None, logger: logging.Logger = logging.getLogger(__name__)):
57 
58     if target_datetime is not None:
59         raise NotImplementedError('The datasource currently implemented is only real time')
60       
61     payload = SignalR("https://data.ajenti.com.au/live/signalr").get_value("TagHub", "Dashboard")
62     technologies_parsed, biodiesel_percent = parse_payload(logger, payload)
63     battery_production, battery_storage = format_storage_techs(technologies_parsed)
64     return {
65       'zoneKey': zone_key,
66       'datetime': arrow.now(tz='Australia/Currie').datetime,
67       'production': {
68           'battery discharge': battery_production,
69           'biomass': technologies_parsed['diesel']*biodiesel_percent/100,
70           'coal': 0,
71           'gas': 0,
72           'hydro': 0,
73           'nuclear': 0,
74           'oil': technologies_parsed['diesel']*(100-biodiesel_percent)/100,
75           'solar': technologies_parsed['solar'],
76           'wind': technologies_parsed['wind'],
77           'geothermal': 0,
78           'unknown': 0
79       },
80       'storage': {
81           'battery': battery_storage*-1
82       },
83       'source': 'https://data.ajenti.com.au/KIREIP/index.html'
84     }
85 
86 if __name__ == '__main__':
87     print(fetch_production())
88
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parsers/AUS_TAS_KI.py b/parsers/AUS_TAS_KI.py
--- a/parsers/AUS_TAS_KI.py
+++ b/parsers/AUS_TAS_KI.py
@@ -34,7 +34,13 @@
         
 def parse_payload(logger, payload):
     technologies_parsed = {}
-    logger.debug(f"serie : {json.dumps(payload)}")
+    if not 'technologies' in payload:
+      raise KeyError(
+        f"No 'technologies' in payload\n"
+        f"serie : {json.dumps(payload)}"
+      )
+    else:
+      logger.debug(f"serie : {json.dumps(payload)}")
     for technology in payload['technologies']:
         assert technology['unit'] == 'kW'
         # The upstream API gives us kW, we need MW
@@ -73,7 +79,7 @@
           'nuclear': 0,
           'oil': technologies_parsed['diesel']*(100-biodiesel_percent)/100,
           'solar': technologies_parsed['solar'],
-          'wind': technologies_parsed['wind'],
+          'wind': 0 if technologies_parsed['wind'] < 0 and technologies_parsed['wind'] > -0.1 else technologies_parsed['wind'], #If wind between 0 and -0.1 set to 0 to ignore self-consumption
           'geothermal': 0,
           'unknown': 0
       },

{"golden_diff": "diff --git a/parsers/AUS_TAS_KI.py b/parsers/AUS_TAS_KI.py\n--- a/parsers/AUS_TAS_KI.py\n+++ b/parsers/AUS_TAS_KI.py\n@@ -34,7 +34,13 @@\n         \n def parse_payload(logger, payload):\n     technologies_parsed = {}\n-    logger.debug(f\"serie : {json.dumps(payload)}\")\n+    if not 'technologies' in payload:\n+      raise KeyError(\n+        f\"No 'technologies' in payload\\n\"\n+        f\"serie : {json.dumps(payload)}\"\n+      )\n+    else:\n+      logger.debug(f\"serie : {json.dumps(payload)}\")\n     for technology in payload['technologies']:\n         assert technology['unit'] == 'kW'\n         # The upstream API gives us kW, we need MW\n@@ -73,7 +79,7 @@\n           'nuclear': 0,\n           'oil': technologies_parsed['diesel']*(100-biodiesel_percent)/100,\n           'solar': technologies_parsed['solar'],\n-          'wind': technologies_parsed['wind'],\n+          'wind': 0 if technologies_parsed['wind'] < 0 and technologies_parsed['wind'] > -0.1 else technologies_parsed['wind'], #If wind between 0 and -0.1 set to 0 to ignore self-consumption\n           'geothermal': 0,\n           'unknown': 0\n       },\n", "issue": "AUS-TAS-KI reports negative wind generation sometimes\nSmall values, so probably self-consumption. I'll prepare a fix\r\n\r\n[Kibana](https://kibana.electricitymap.org/app/kibana#/discover/10af54f0-0c4a-11e9-85c1-1d63df8c862c?_g=(refreshInterval:('$$hashKey':'object:232',display:'5%20minutes',pause:!f,section:2,value:300000),time:(from:now-24h,mode:quick,to:now))&_a=(columns:!(message,extra.key,level),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:level,negate:!f,params:(query:ERROR,type:phrase),type:phrase,value:ERROR),query:(match:(level:(query:ERROR,type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:extra.key,negate:!f,params:(query:AUS-TAS-KI,type:phrase),type:phrase,value:AUS-TAS-KI),query:(match:(extra.key:(query:AUS-TAS-KI,type:phrase))))),index:'96f67170-0c49-11e9-85c1-1d63df8c862c',interval:auto,query:(language:lucene,query:''),sort:!('@timestamp',desc))):\r\n\r\n>November 17th 2020, 17:23:16.743\r\ninvalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 23, 16, 425060, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.338, 'solar': 0.0, 'wind': -0.003, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.003\r\nAUS-TAS-KI\r\nERROR\r\n>November 17th 2020, 17:02:28.794invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 2, 27, 648934, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.217, 'solar': 0.0, 'wind': -0.006, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.006\r\nAUS-TAS-KI\r\nERROR\r\n>November 17th 2020, 16:49:52.790invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 2, 49, 49, 351083, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.264, 'solar': 0.0, 'wind': -0.017, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.017\r\nAUS-TAS-KI\r\nERROR\nAUS-TAS-KI reports negative wind generation sometimes\nSmall values, so probably self-consumption. I'll prepare a fix\r\n\r\n[Kibana](https://kibana.electricitymap.org/app/kibana#/discover/10af54f0-0c4a-11e9-85c1-1d63df8c862c?_g=(refreshInterval:('$$hashKey':'object:232',display:'5%20minutes',pause:!f,section:2,value:300000),time:(from:now-24h,mode:quick,to:now))&_a=(columns:!(message,extra.key,level),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:level,negate:!f,params:(query:ERROR,type:phrase),type:phrase,value:ERROR),query:(match:(level:(query:ERROR,type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'96f67170-0c49-11e9-85c1-1d63df8c862c',key:extra.key,negate:!f,params:(query:AUS-TAS-KI,type:phrase),type:phrase,value:AUS-TAS-KI),query:(match:(extra.key:(query:AUS-TAS-KI,type:phrase))))),index:'96f67170-0c49-11e9-85c1-1d63df8c862c',interval:auto,query:(language:lucene,query:''),sort:!('@timestamp',desc))):\r\n\r\n>November 17th 2020, 17:23:16.743\r\ninvalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 23, 16, 425060, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.338, 'solar': 0.0, 'wind': -0.003, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.003\r\nAUS-TAS-KI\r\nERROR\r\n>November 17th 2020, 17:02:28.794invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 3, 2, 27, 648934, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.217, 'solar': 0.0, 'wind': -0.006, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.006\r\nAUS-TAS-KI\r\nERROR\r\n>November 17th 2020, 16:49:52.790invalid point: {'zoneKey': 'AUS-TAS-KI', 'datetime': datetime.datetime(2020, 11, 18, 2, 49, 49, 351083, tzinfo=tzfile('/usr/share/zoneinfo/Australia/Currie')), 'production': {'battery discharge': 0, 'biomass': 0.0, 'coal': 0, 'gas': 0, 'hydro': 0, 'nuclear': 0, 'oil': 1.264, 'solar': 0.0, 'wind': -0.017, 'geothermal': 0, 'unknown': 0}, 'storage': {'battery': 0.004}, 'source': 'https://data.ajenti.com.au/KIREIP/index.html', 'schemaVersion': 3}, reason:AUS-TAS-KI: key wind has negative value -0.017\r\nAUS-TAS-KI\r\nERROR\n", "before_files": [{"content": "# Initial PR https://github.com/tmrowco/electricitymap-contrib/pull/2456\n# Discussion thread https://github.com/tmrowco/electricitymap-contrib/issues/636\n# A promotion webpage for King's Island energy production is here : https://www.hydro.com.au/clean-energy/hybrid-energy-solutions/success-stories/king-island\n# As of 09/2020, it embeds with <iframe> the URI https://data.ajenti.com.au/KIREIP/index.html\n# About the data, the feed we get seems to be counters with a 2 seconds interval.\n# That means that if we fetch these counters every 15 minutes, we only are reading \"instantaneous\" metters that could differ from the total quantity of energies at play. To get the very exact data, we would need to have a parser running constanty to collect those 2-sec interval counters.\n\nimport asyncio\nimport json\nimport logging\nimport arrow\nfrom signalr import Connection\nfrom requests import Session\n\nclass SignalR:\n    def __init__(self, url):\n        self.url = url\n    \n    def update_res(self, msg):\n        if (msg != {}):\n            self.res = msg\n\n    def get_value(self, hub, method):\n        self.res = {}\n        with Session() as session:\n            #create a connection\n            connection = Connection(self.url, session)\n            chat = connection.register_hub(hub)\n            chat.client.on(method, self.update_res)\n            connection.start()\n            connection.wait(3)\n            connection.close()\n            return self.res\n        \ndef parse_payload(logger, payload):\n    technologies_parsed = {}\n    logger.debug(f\"serie : {json.dumps(payload)}\")\n    for technology in payload['technologies']:\n        assert technology['unit'] == 'kW'\n        # The upstream API gives us kW, we need MW\n        technologies_parsed[technology['id']] = int(technology['value'])/1000\n    logger.debug(f\"production : {json.dumps(technologies_parsed)}\")\n\n    biodiesel_percent = payload['biodiesel']['percent']\n\n    return technologies_parsed, biodiesel_percent\n\n# Both keys battery and flywheel are negative when storing energy, and positive when feeding energy to the grid\ndef format_storage_techs(technologies_parsed):\n    storage_techs = technologies_parsed['battery']+technologies_parsed['flywheel']\n    battery_production = storage_techs if storage_techs > 0 else 0\n    battery_storage = storage_techs if storage_techs < 0 else 0\n\n    return battery_production, battery_storage\n\ndef fetch_production(zone_key='AUS-TAS-KI', session=None, target_datetime=None, logger: logging.Logger = logging.getLogger(__name__)):\n\n    if target_datetime is not None:\n        raise NotImplementedError('The datasource currently implemented is only real time')\n      \n    payload = SignalR(\"https://data.ajenti.com.au/live/signalr\").get_value(\"TagHub\", \"Dashboard\")\n    technologies_parsed, biodiesel_percent = parse_payload(logger, payload)\n    battery_production, battery_storage = format_storage_techs(technologies_parsed)\n    return {\n      'zoneKey': zone_key,\n      'datetime': arrow.now(tz='Australia/Currie').datetime,\n      'production': {\n          'battery discharge': battery_production,\n          'biomass': technologies_parsed['diesel']*biodiesel_percent/100,\n          'coal': 0,\n          'gas': 0,\n          'hydro': 0,\n          'nuclear': 0,\n          'oil': technologies_parsed['diesel']*(100-biodiesel_percent)/100,\n          'solar': technologies_parsed['solar'],\n          'wind': technologies_parsed['wind'],\n          'geothermal': 0,\n          'unknown': 0\n      },\n      'storage': {\n          'battery': battery_storage*-1\n      },\n      'source': 'https://data.ajenti.com.au/KIREIP/index.html'\n    }\n\nif __name__ == '__main__':\n    print(fetch_production())\n", "path": "parsers/AUS_TAS_KI.py"}], "after_files": [{"content": "# Initial PR https://github.com/tmrowco/electricitymap-contrib/pull/2456\n# Discussion thread https://github.com/tmrowco/electricitymap-contrib/issues/636\n# A promotion webpage for King's Island energy production is here : https://www.hydro.com.au/clean-energy/hybrid-energy-solutions/success-stories/king-island\n# As of 09/2020, it embeds with <iframe> the URI https://data.ajenti.com.au/KIREIP/index.html\n# About the data, the feed we get seems to be counters with a 2 seconds interval.\n# That means that if we fetch these counters every 15 minutes, we only are reading \"instantaneous\" metters that could differ from the total quantity of energies at play. To get the very exact data, we would need to have a parser running constanty to collect those 2-sec interval counters.\n\nimport asyncio\nimport json\nimport logging\nimport arrow\nfrom signalr import Connection\nfrom requests import Session\n\nclass SignalR:\n    def __init__(self, url):\n        self.url = url\n    \n    def update_res(self, msg):\n        if (msg != {}):\n            self.res = msg\n\n    def get_value(self, hub, method):\n        self.res = {}\n        with Session() as session:\n            #create a connection\n            connection = Connection(self.url, session)\n            chat = connection.register_hub(hub)\n            chat.client.on(method, self.update_res)\n            connection.start()\n            connection.wait(3)\n            connection.close()\n            return self.res\n        \ndef parse_payload(logger, payload):\n    technologies_parsed = {}\n    if not 'technologies' in payload:\n      raise KeyError(\n        f\"No 'technologies' in payload\\n\"\n        f\"serie : {json.dumps(payload)}\"\n      )\n    else:\n      logger.debug(f\"serie : {json.dumps(payload)}\")\n    for technology in payload['technologies']:\n        assert technology['unit'] == 'kW'\n        # The upstream API gives us kW, we need MW\n        technologies_parsed[technology['id']] = int(technology['value'])/1000\n    logger.debug(f\"production : {json.dumps(technologies_parsed)}\")\n\n    biodiesel_percent = payload['biodiesel']['percent']\n\n    return technologies_parsed, biodiesel_percent\n\n# Both keys battery and flywheel are negative when storing energy, and positive when feeding energy to the grid\ndef format_storage_techs(technologies_parsed):\n    storage_techs = technologies_parsed['battery']+technologies_parsed['flywheel']\n    battery_production = storage_techs if storage_techs > 0 else 0\n    battery_storage = storage_techs if storage_techs < 0 else 0\n\n    return battery_production, battery_storage\n\ndef fetch_production(zone_key='AUS-TAS-KI', session=None, target_datetime=None, logger: logging.Logger = logging.getLogger(__name__)):\n\n    if target_datetime is not None:\n        raise NotImplementedError('The datasource currently implemented is only real time')\n      \n    payload = SignalR(\"https://data.ajenti.com.au/live/signalr\").get_value(\"TagHub\", \"Dashboard\")\n    technologies_parsed, biodiesel_percent = parse_payload(logger, payload)\n    battery_production, battery_storage = format_storage_techs(technologies_parsed)\n    return {\n      'zoneKey': zone_key,\n      'datetime': arrow.now(tz='Australia/Currie').datetime,\n      'production': {\n          'battery discharge': battery_production,\n          'biomass': technologies_parsed['diesel']*biodiesel_percent/100,\n          'coal': 0,\n          'gas': 0,\n          'hydro': 0,\n          'nuclear': 0,\n          'oil': technologies_parsed['diesel']*(100-biodiesel_percent)/100,\n          'solar': technologies_parsed['solar'],\n          'wind': 0 if technologies_parsed['wind'] < 0 and technologies_parsed['wind'] > -0.1 else technologies_parsed['wind'], #If wind between 0 and -0.1 set to 0 to ignore self-consumption\n          'geothermal': 0,\n          'unknown': 0\n      },\n      'storage': {\n          'battery': battery_storage*-1\n      },\n      'source': 'https://data.ajenti.com.au/KIREIP/index.html'\n    }\n\nif __name__ == '__main__':\n    print(fetch_production())\n", "path": "parsers/AUS_TAS_KI.py"}]}