Plugin help

Posted on
Thu Dec 28, 2023 4:12 pm
mwoodage offline
User avatar
Posts: 174
Joined: Dec 19, 2014
Location: Devon UK

Plugin help

Hi Everyone,
I've thought about this for years and had some free time over Christmas and New Year, so decided to give it a try and make my first indigo plugin, however i'm finding it really hard going so asking for some help. (BTW I have no programming experience, so mainly copying bits from the Plugin SDK's and from other plugins (mainly FlyingDivers ConnectedDrive, i hope that's ok FD?)) I used the ConnectedDrive plugin as it's very similar to what i'm trying to do, only it's for BMW's and mine is for Toyota.

What i've been trying to do is make a plugin (for my own use only) that will capture my Toyota RAV4's battery and fuel data, so i can show this detail on a control page.
I've been following the development of some Toyota code on GitHub (https://github.com/DurgNomis-drol/mytoyota) which can call my car and provide the data required along with lots of other stuff. And i can get this to work as shown below using terminal.
So, my thought was to see if i can integrate this into Indigo, and after several days of playing around, I now have a basic plugin, but no data.

In order to move forward, my question is, how do i get the data that's being produced by the Toyota GitHub script to show up in Indigo via the plugin?
So far i've managed to create a plugin folder and get a device window into indigo, but now i'm stuck! how do you get the created Device to call for the data in the plugin.py script?

For further information I've copied the Github code to my indigo computer and in terminal I call
Code: Select all
python3 simple_client_test.py
which gives the results show below.
Screenshot 2023-12-28 at 21.34.50.png
Screenshot 2023-12-28 at 21.34.50.png (63.74 KiB) Viewed 989 times

I've then taken the simple_client_test.py code and renamed it
Code: Select all
plugin.py
, is that right, will that work??

Not sure if i'm going down the right route or if there is an easier way of doing this. I'm also aware that i should probably ask DurgNomis if i can use his code in this way, should i do that first??

Hopefully someone can point me in the right direction,

Thanks in advance,
Martin
Attachments
Screenshot 2023-12-28 at 21.45.23.png
Screenshot 2023-12-28 at 21.45.23.png (67.93 KiB) Viewed 989 times
Screenshot 2023-12-28 at 21.43.56.png
Screenshot 2023-12-28 at 21.43.56.png (26.27 KiB) Viewed 989 times

Posted on
Thu Dec 28, 2023 4:38 pm
ryanbuckner offline
Posts: 1080
Joined: Oct 08, 2011
Location: Northern Virginia

Re: Plugin help

Have you spent any time looking at the plugin tutorial? Start by creating a device and a few custom device states to hold the data points you want to have in indigo.

Using the polling frequency in the plugin config (similar to the way Flying Diver does his), you'll call the toyota api. Then you'll cycle through the JSON file and pull the relevant data out, and assign it to your device's custom device states.

Code: Select all
cars = await client.get_vehicles(metric=False)

## non working example
await car[0].update()

##assumes you have a custom state lock_status defined
device_states.append({'key': 'lock_status','value': car[0].lock_status })

## updates the custom states on the indigo server
device.updateStatesOnServer(device_states)


Posted on
Fri Dec 29, 2023 10:09 am
neilk offline
Posts: 715
Joined: Jul 13, 2015
Location: Reading, UK

Re: Plugin help

Martin,

Making the leap to a plugin might be too big a first step, but you can achieve what you are looking to do with a script that runs on a schedule.

You don't need permission to utilise the code as it is released under the MIT license, and you can change it so that it sets Indigo variables which you can then show on a control page.

Once that is working you could look at a really simple plugin (my Solcast one is about as simple as you can get) and you can shift to a real plugin, but it is a lot more involved than renaming the script.

Let's look at the test script (I am not sure if that is the one you are running)

Code: Select all
import json
import asyncio
from mytoyota.client import MyT

username = "jane@doe.com"
password = "MyPassword"
brand = "toyota"  # or lexus

# Get supported regions, can be passed to the optional 'region' argument of MyT
# At this moment, only the 'europe' region is supported
print(MyT.get_supported_regions())

client = MyT(username=username, password=password, brand=brand)

async def get_information():
    print("Logging in...")
    await client.login()

    print("Retrieving cars...")
    # Returns cars registered to your account + information about each car.
    cars = await client.get_vehicles()

    for car in cars:

        # Returns live data from car/last time you used it as an object.
        vehicle = await client.get_vehicle_status(car)

        # You can either get them all async (Recommended) or sync (Look further down).
        data = await asyncio.gather(
            *[
                client.get_driving_statistics(vehicle.vin, interval="day"),
                client.get_driving_statistics(vehicle.vin, interval="isoweek"),
                client.get_driving_statistics(vehicle.vin),
                client.get_driving_statistics(vehicle.vin, interval="year"),
            ]
        )

        # You can access odometer data like this:
        mileage = vehicle.dashboard.odometer
        # Or retrieve the energy level (electric or gasoline)
        fuel = vehicle.dashboard.fuel_level
        battery = vehicle.dashboard.battery_level
        # Or Parking information:
       
        # Daily stats
        daily_stats = await client.get_driving_statistics(vehicle.vin, interval="day")

        # ISO 8601 week stats
        iso_weekly_stats = await client.get_driving_statistics(vehicle.vin, interval="isoweek")

        # Monthly stats is returned by default
        monthly_stats = await client.get_driving_statistics(vehicle.vin)

        # Get year to date stats.
        yearly_stats = await client.get_driving_statistics(vehicle.vin, interval="year")


loop = asyncio.get_event_loop()
loop.run_until_complete(get_information())
loop.close()



If you change the "print" to "indigo.server.log" then the output will go into the server log. Trigger the script from an action group and you should see the output when you run the AG in the server log. (Run it as an "Execute Script" from a file.

If that works then we can go about getting the specific values you want, first try just printing them to the server log

Code: Select all
# You can access odometer data like this:
        mileage = vehicle.dashboard.odometer
        # Or retrieve the energy level (electric or gasoline)
        fuel = vehicle.dashboard.fuel_level
        battery = vehicle.dashboard.battery_level
        indigo.server.log("Mileage is "+str(mileage))
        indigo.server.log("Fuel is "+str(fuel))
        indigo.server.log("Battery is "+str(battery))
        # Or Parking information:
        latitude = vehicle.parkinglocation.latitude



The "str" command converts what I suspect the code delivers as an Integer to a string, so we can print it to the log (I cannot try it)

If this works the next step would be to save that value to a variable

first create the variable you want, then add the following lines with the correct variable ID that you created. You could do the same for the other values

Code: Select all
mileageVar = indigo.variables[1893500335] # always use variable ID rather than name
indigo.variable.updateValue(mileageVar, str(mileage))


You can also strip out the parts you don't need.

Does that help ?

Neil

Posted on
Fri Dec 29, 2023 11:38 am
jay (support) offline
Site Admin
User avatar
Posts: 18224
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Plugin help

I think Neil's suggestion is a good one, but I'm not positive that running the script (you'll have to run it as an external script) will actually work because of the asyncio implementation that the script uses. It might (there are plugins that use asyncio successfully, but this is a script not a plugin), or it might not (because of how we run Indigo python scripts). I would just try it first with the changes Neil suggests to just get a first attempt.

If it doesn't work running it as a linked script, then you have a couple of options:

1) Dig into his library to get to the actual network calls and do them yourself in a script (and eventually plugin).
2) Modify his script so that it uses the HTTP API to communicate with Indigo, then just run the script directly from python (we can show you how to do that).

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Fri Dec 29, 2023 1:07 pm
mwoodage offline
User avatar
Posts: 174
Joined: Dec 19, 2014
Location: Devon UK

Re: Plugin help

Wow! thanks guys and thank you Neil,
I've made progress with changing the "print" to "indigo.server.log", this provides the following info in the log whenever i restart the plugin.

Code: Select all
Z-Wave                          sent "Utility Room Lights" set brightness to 95
   Z-Wave                          received "Utility Room Motion Sensor" status update is off
   Trigger                         Utility Room - Light Off
   Reloading plugin "Toyota 2022.1.0" using API v3.0
   Stopping plugin "Toyota 2022.1.0" (pid 14298)
   Toyota Debug                    stop_concurrent_thread called
   Stopped plugin "Toyota 2022.1.0"
   Starting plugin "Toyota 2022.1.0" (pid 14587)
   Toyota                          Logging in...
   Toyota                          Retrieving cars...
   Toyota                          Alias: JTMGBRFV
   Toyota                          Mileage : 4431
   Toyota                          Fuel    : 69
   Toyota                          Battery : 36
   Toyota                          Latitude : 50.63685
   Started plugin "Toyota 2022.1.0"
   Z-Wave                          received "Utility Room Motion Sensor" status update is on


So i then tried to Trigger the script from an action group, but was unable to do this. First i tried to run the file
simple_client_test.py


from the location where Github had downloaded all the files
/Users/me/Documents/GitHub/mytoyota


And got the following error in the log,
Code: Select all
Action Group                    Toyota Trial
   Script Error                    action group "Toyota Trial" script error in file simple_client_test.py:
   Script Error                    HTTP: 400 - {"code":"TME_B2C_ERR_UNHANDLED_ERROR","message":"Something went very very wrong! :-( due to TAAS response: Bad Request","stack":"AppError: Something went very very wrong! :-( due to TAAS response: Bad Request\n    at defaultHandler (/var/lib/nodejs/src/lib/error.js:61:19)\n    at /var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/router.js:50:25\n    at defaultHandler (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-shimmer/src/shimmer.js:26:21)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:71:5)\n    at trim_prefix (/var/lib/nodejs/node_modules/express/lib/router/index.js:326:13)\n    at /var/lib/nodejs/node_modules/express/lib/router/index.js:286:9\n    at Function.process_params (/var/lib/nodejs/node_modules/express/lib/router/index.js:346:12)\n    at Function.process_params (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/express.js:70:23)\n    at next (/var/lib/nodejs/node_modules/express/lib/router/index.js:280:10)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:67:12)","name":"AppError","info":{"code":"TAAS_SERVICE_STATUS_400","message":"TAAS response: Bad Request","stack":"TmeError: TAAS response: Bad Request\n    at Taas._replyToTaasError (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:362:23)\n    at Taas.getUserByToken (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:184:40)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async TaasStrategy.deserializeUser (/var/lib/nodejs/node_modules/@tme/taas-passport/lib/taas-strategy.js:69:26)","name":"TmeError","data":{"body":{"httpErrorCode":400,"message":"Brand provided is not recognised"},"path":"/user/getUserByToken"},"status":400,"source":"TAAS"},"statusCode":400}
   Script Error                    Exception Traceback (most recent call shown last):
     simple_client_test.py, line 74, at top level
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py', line 641, in run_until_complete
       return future.result()
     simple_client_test.py, line 43, in get_information
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/mytoyota/client.py', line 212, in get_vehicles
       vehicles = await self.api.get_vehicles_endpoint()
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/mytoyota/api.py', line 35, in get_vehicles_endpoint
       return await self.controller.request(
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/mytoyota/controller.py', line 245, in request
       raise ToyotaApiError(
ToyotaApiError: HTTP: 400 - {"code":"TME_B2C_ERR_UNHANDLED_ERROR","message":"Something went very very wrong! :-( due to TAAS response: Bad Request","stack":"AppError: Something went very very wrong! :-( due to TAAS response: Bad Request\n    at defaultHandler (/var/lib/nodejs/src/lib/error.js:61:19)\n    at /var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/router.js:50:25\n    at defaultHandler (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-shimmer/src/shimmer.js:26:21)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:71:5)\n    at trim_prefix (/var/lib/nodejs/node_modules/express/lib/router/index.js:326:13)\n    at /var/lib/nodejs/node_modules/express/lib/router/index.js:286:9\n    at Function.process_params (/var/lib/nodejs/node_modules/express/lib/router/index.js:346:12)\n    at Function.process_params (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/express.js:70:23)\n    at next (/var/lib/nodejs/node_modules/express/lib/router/index.js:280:10)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:67:12)","name":"AppError","info":{"code":"TAAS_SERVICE_STATUS_400","message":"TAAS response: Bad Request","stack":"TmeError: TAAS response: Bad Request\n    at Taas._replyToTaasError (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:362:23)\n    at Taas.getUserByToken (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:184:40)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async TaasStrategy.deserializeUser (/var/lib/nodejs/node_modules/@tme/taas-passport/lib/taas-strategy.js:69:26)","name":"TmeError","data":{"body":{"httpErrorCode":400,"message":"Brand provided is not recognised"},"path":"/user/getUserByToken"},"status":400,"source":"TAAS"},"statusCode":400}


And then tried it from the scrips folder location and got similar results.

So not sure what the next step would be, but I'm very encouraged that it's at least appearing in the log - that massive progress for me :D :D

BTW, this is the code i'm using, it' slightly different from the one you've found - it's a few weeks older than the latest version.

Code: Select all
import asyncio
import json
import pprint

from mytoyota.client import MyT

# Set your username and password here OR
# in a file called credentials.json in the format
#   {
#       "username": "<username>",
#       "password": "<password>"
#   }
username = '***********'
password = '***********'
try:
    credentials = json.load(open("credentials.json"))
    username = credentials["username"]
    password = credentials["password"]
except FileNotFoundError:
    pass
except json.decoder.JSONDecodeError:
    pass

if username is None or password is None:
    indigo.server.log(
        "Did you forget to set your username and password? Or supply the credentials file"
    )
    exit()

# Pretty indigo.server.loger used below
pp = pprint.PrettyPrinter(indent=4)


client = MyT(username=username, password=password, brand="T")


async def get_information():
    indigo.server.log("Logging in...")
    await client.login()

    indigo.server.log("Retrieving cars...")
    # Returns cars registered to your account + information about each car.
    cars = await client.get_vehicles()

    for car in cars:
        await car.update()

        # Alias
        indigo.server.log(f"Alias: {car.alias}")
        # Set alis
        # await car.set_alias("RAV4")

        # Basic information
        mileage = car.dashboard.odometer
        indigo.server.log(f"Mileage : {mileage}")
        # Or retrieve the energy level (electric or gasoline)
        fuel = car.dashboard.fuel_level
        indigo.server.log(f"Fuel    : {fuel}")
        battery = car.dashboard.battery_level
        indigo.server.log(f"Battery : {battery}")
        # Or Parking information:
        latitude = car.location.latitude
        indigo.server.log(f"Latitude : {latitude}")

        # Notifications => True retrieve all, False just unread
        # notifications = car.notifications(True)[:5]
        # if notifications:
          #  indigo.server.log("Notifications:")
           # for notification in notifications:
            #    indigo.server.log(f"    {notification.date} : {notification.message}")


loop = asyncio.get_event_loop()
loop.run_until_complete(get_information())
loop.close()

Posted on
Sat Dec 30, 2023 6:26 am
neilk offline
Posts: 715
Joined: Jul 13, 2015
Location: Reading, UK

Re: Plugin help

Martin - I have no real understanding of the asyncio approach that the sample script uses, but I think as Jay says that will be the cause of the issue you are seeing. I think the best approach will be to use the approach (2) Jay mentioned in a standalone python script. Once we have that running and getting data into Indigo we can look at moving the plugin forward.

My journey into learning to write plugins started the same way, so I think it is a good approach, but I am afraid I had to bite the bullet and complete the online python course before I could get to my pretty basic level. I started the same by stitching together other peoples stuff and it is a good way to learn.

We should start with the original script, and modify that to use the http api to set a variable, it will be one step backwards before one forwards but lets see if we can make it work standalone first.

I cannot test this but it should be close, it uses the example code from the HTTP API documentation https://wiki.indigodomo.com/doku.php?id=indigo_2022.2_documentation:api#http_api

You will need to have setup an API key, and created variables for fuel, battery, latitude and mileage adding in the script the correct variable ID;

Code: Select all
import asyncio

# Adding the import line from the variable example from the new HTTP api "Updating a variables value" and removed the pretty print as it won't be needed
from urllib.request import Request, urlopen
import json
from mytoyota.client import MyT


# Again from the example add your details below, including the correct variable ID for mileage, fuel, battery and latitude
REFLECTORNAME = "YOUR-REFLECTOR-NAME"
APIKEY = "YOUR-API-KEY"
MILEAGEVARIABLE = 123456789
FUELVARIABLE = 123456789
BATTERYVARIABLE = 123456789
LATITUDEVARIABLE = 123456789


# now define a function (so we don't need to repeat the code to set each variable, we will call it later but it is the code from the http api python example

def set_indigo_variable(VARIABLEID, VALUE):
    message = json.dumps({
        "id": "optional-user-generated-id",
        "message": "indigo.variable.updateValue",
        "objectId": VARIABLEID,
        "parameters": {
            # this is modified to use VALUE that is passed to the function to updated the indigo variable rather than "Some string value" in the example code
            "value": VALUE
        }
    }).encode("utf8")
    req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message)
    req.add_header('Authorization', f"Bearer {APIKEY}")
    with urlopen(req) as request:
        reply = json.load(request)
        print(reply)



# Set your username and password here OR
# in a file called credentials.json in the format
#   {
#       "username": "<username>",
#       "password": "<password>"
#   }
username = '***********'
password = '***********'
try:
    credentials = json.load(open("credentials.json"))
    username = credentials["username"]
    password = credentials["password"]
except FileNotFoundError:
    pass
except json.decoder.JSONDecodeError:
    pass

if username is None or password is None:
    print(
        "Did you forget to set your username and password? Or supply the credentials file"
    )
    exit()

# Pretty indigo.server.loger used below

client = MyT(username=username, password=password, brand="T")


async def get_information():
    print("Logging in...")
    await client.login()

    print("Retrieving cars...")
    # Returns cars registered to your account + information about each car.
    cars = await client.get_vehicles()

    for car in cars:
        await car.update()

        # Alias
        print("Alias: "+car.alias)
        # Set alias
        # await car.set_alias("RAV4")

        # Basic information
        mileage = car.dashboard.odometer
        print("Mileage "+mileage)
        # now call the function passing it the correct variable ID and the mileage value returned (Same approach for the others)
        set_indigo_variable(MILEAGEVARIABLE, mileage)
        # Or retrieve the energy level (electric or gasoline)
        fuel = car.dashboard.fuel_level
        print("Fuel :"+fuel)
        set_indigo_variable(FUELVARIABLE, fuel)
        battery = car.dashboard.battery_level
        print("Battery : "+battery)
        set_indigo_variable(BATTERYVARIABLE, battery)
        # Or Parking information:
        latitude = car.location.latitude
        print("Latitude :"+ latitude)
        set_indigo_variable(LATITUDEVARIABLE, latitude)

        # Notifications => True retrieve all, False just unread
        # notifications = car.notifications(True)[:5]
        # if notifications:
        #  indigo.server.log("Notifications:")
        # for notification in notifications:
        #    indigo.server.log(f"    {notification.date} : {notification.message}")


loop = asyncio.get_event_loop()
loop.run_until_complete(get_information())
loop.close()


If this works then we can move on to the next step. I guess you will need to move/charge your car to be certain :-)

Neil

Posted on
Sat Dec 30, 2023 11:53 am
mwoodage offline
User avatar
Posts: 174
Joined: Dec 19, 2014
Location: Devon UK

Re: Plugin help

Hi Neil,
I've given this a go and i'm getting the following errors when i run the script. I've setup the variable ID's and added these, i've called them, (all lowercase) mileage, fuel, battery and latitude, is that correct?

MILEAGEVARIABLE = 924494111
FUELVARIABLE = 1757439952
BATTERYVARIABLE = 1970916222
LATITUDEVARIABLE = 998494523

I've also added in my Reflector Name and API key, shown as stars below.

Any ideas on the error shown?

Code: Select all
Action Group                    Toyota Trial
   Script Error                    action group "Toyota Trial" script error in file plugin.py:
   Script Error                    HTTP: 400 - {"code":"TME_B2C_ERR_UNHANDLED_ERROR","message":"Something went very very wrong! :-( due to TAAS response: Bad Request","stack":"AppError: Something went very very wrong! :-( due to TAAS response: Bad Request\n    at defaultHandler (/var/lib/nodejs/src/lib/error.js:61:19)\n    at /var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/router.js:50:25\n    at defaultHandler (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-shimmer/src/shimmer.js:26:21)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:71:5)\n    at trim_prefix (/var/lib/nodejs/node_modules/express/lib/router/index.js:326:13)\n    at /var/lib/nodejs/node_modules/express/lib/router/index.js:286:9\n    at Function.process_params (/var/lib/nodejs/node_modules/express/lib/router/index.js:346:12)\n    at Function.process_params (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/express.js:70:23)\n    at next (/var/lib/nodejs/node_modules/express/lib/router/index.js:280:10)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:67:12)","name":"AppError","info":{"code":"TAAS_SERVICE_STATUS_400","message":"TAAS response: Bad Request","stack":"TmeError: TAAS response: Bad Request\n    at Taas._replyToTaasError (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:362:23)\n    at Taas.getUserByToken (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:184:40)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async TaasStrategy.deserializeUser (/var/lib/nodejs/node_modules/@tme/taas-passport/lib/taas-strategy.js:69:26)","name":"TmeError","data":{"body":{"httpErrorCode":400,"message":"Brand provided is not recognised"},"path":"/user/getUserByToken"},"status":400,"source":"TAAS"},"statusCode":400}
   Script Error                    Exception Traceback (most recent call shown last):

     plugin.py, line 109, at top level
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py', line 641, in run_until_complete
       return future.result()
     plugin.py, line 73, in get_information
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/mytoyota/client.py', line 212, in get_vehicles
       vehicles = await self.api.get_vehicles_endpoint()
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/mytoyota/api.py', line 35, in get_vehicles_endpoint
       return await self.controller.request(
     File '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/mytoyota/controller.py', line 245, in request
       raise ToyotaApiError(
ToyotaApiError: HTTP: 400 - {"code":"TME_B2C_ERR_UNHANDLED_ERROR","message":"Something went very very wrong! :-( due to TAAS response: Bad Request","stack":"AppError: Something went very very wrong! :-( due to TAAS response: Bad Request\n    at defaultHandler (/var/lib/nodejs/src/lib/error.js:61:19)\n    at /var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/router.js:50:25\n    at defaultHandler (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-shimmer/src/shimmer.js:26:21)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:71:5)\n    at trim_prefix (/var/lib/nodejs/node_modules/express/lib/router/index.js:326:13)\n    at /var/lib/nodejs/node_modules/express/lib/router/index.js:286:9\n    at Function.process_params (/var/lib/nodejs/node_modules/express/lib/router/index.js:346:12)\n    at Function.process_params (/var/lib/nodejs/node_modules/@tme/datadog/node_modules/dd-trace/packages/datadog-instrumentations/src/express.js:70:23)\n    at next (/var/lib/nodejs/node_modules/express/lib/router/index.js:280:10)\n    at Layer.handle_error (/var/lib/nodejs/node_modules/express/lib/router/layer.js:67:12)","name":"AppError","info":{"code":"TAAS_SERVICE_STATUS_400","message":"TAAS response: Bad Request","stack":"TmeError: TAAS response: Bad Request\n    at Taas._replyToTaasError (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:362:23)\n    at Taas.getUserByToken (/var/lib/nodejs/node_modules/@tme/taas/lib/taas.js:184:40)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async TaasStrategy.deserializeUser (/var/lib/nodejs/node_modules/@tme/taas-passport/lib/taas-strategy.js:69:26)","name":"TmeError","data":{"body":{"httpErrorCode":400,"message":"Brand provided is not recognised"},"path":"/user/getUserByToken"},"status":400,"source":"TAAS"},"statusCode":400}




Code: Select all
import asyncio

# Adding the import line from the variable example from the new HTTP api "Updating a variables value" and removed the pretty print as it won't be needed
from urllib.request import Request, urlopen
import json
from mytoyota.client import MyT


# Again from the example add your details below, including the correct variable ID for mileage, fuel, battery and latitude
REFLECTORNAME = "http://*********.indigodomo.net"
APIKEY = "**************"
MILEAGEVARIABLE = 924494111
FUELVARIABLE = 1757439952
BATTERYVARIABLE = 1970916222
LATITUDEVARIABLE = 998494523


# now define a function (so we don't need to repeat the code to set each variable, we will call it later but it is the code from the http api python example

def set_indigo_variable(VARIABLEID, VALUE):
    message = json.dumps({
        "id": "optional-user-generated-id",
        "message": "indigo.variable.updateValue",
        "objectId": VARIABLEID,
        "parameters": {
            # this is modified to use VALUE that is passed to the function to updated the indigo variable rather than "Some string value" in the example code
            "value": VALUE
        }
    }).encode("utf8")
    req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message)
    req.add_header('Authorization', f"Bearer {APIKEY}")
    with urlopen(req) as request:
        reply = json.load(request)
        print(reply)



# Set your username and password here OR
# in a file called credentials.json in the format
#   {
#       "username": "<username>",
#       "password": "<password>"
#   }
username = '***********'
password = '***********'
try:
    credentials = json.load(open("credentials.json"))
    username = credentials["username"]
    password = credentials["password"]
except FileNotFoundError:
    pass
except json.decoder.JSONDecodeError:
    pass

if username is None or password is None:
    print(
        "Did you forget to set your username and password? Or supply the credentials file"
    )
    exit()

# Pretty indigo.server.loger used below

client = MyT(username=username, password=password, brand="T")


async def get_information():
    print("Logging in...")
    await client.login()

    print("Retrieving cars...")
    # Returns cars registered to your account + information about each car.
    cars = await client.get_vehicles()

    for car in cars:
        await car.update()

        # Alias
        print("Alias: "+car.alias)
        # Set alias
        # await car.set_alias("RAV4")

        # Basic information
        mileage = car.dashboard.odometer
        print("Mileage "+mileage)
        # now call the function passing it the correct variable ID and the mileage value returned (Same approach for the others)
        set_indigo_variable(MILEAGEVARIABLE, mileage)
        # Or retrieve the energy level (electric or gasoline)
        fuel = car.dashboard.fuel_level
        print("Fuel :"+fuel)
        set_indigo_variable(FUELVARIABLE, fuel)
        battery = car.dashboard.battery_level
        print("Battery : "+battery)
        set_indigo_variable(BATTERYVARIABLE, battery)
        # Or Parking information:
        latitude = car.location.latitude
        print("Latitude :"+ latitude)
        set_indigo_variable(LATITUDEVARIABLE, latitude)

        # Notifications => True retrieve all, False just unread
        # notifications = car.notifications(True)[:5]
        # if notifications:
        #  indigo.server.log("Notifications:")
        # for notification in notifications:
        #    indigo.server.log(f"    {notification.date} : {notification.message}")


loop = asyncio.get_event_loop()
loop.run_until_complete(get_information())
loop.close()


Thanks
Martin

Posted on
Sun Dec 31, 2023 12:47 am
neilk offline
Posts: 715
Joined: Jul 13, 2015
Location: Reading, UK

Re: Plugin help

Hi Martin - could you try running it from the command line outside of Indigo first (this is the same issue that we saw before that Jay mentioned). If that works (or has a different error due to my addition which we can fix) we can then sort running it from Indigo . Just do it the same way you did for the downloaded script, and if all good your variables should update.

The variable names are fine.
This all relates to the issue Jay raised about the way this script and the associated python module works, so what we are doing is running it outside of Indigo then using the API to update the variables. We can then setup a way to make this run on a schedule which will require a few extra steps and a slightly different method but easy to do.

First add the following as the first line of the script

Code: Select all
#!/usr/bin/env python3



Then in a terminal. (Using the correct file name.)
Code: Select all
chmod +x myfile.py



You should now be able in the terminal to run it by typing just the python file name, finally change your action group to run it as a shell script rather than python ( confusing but that first line we added makes that work).

Posted on
Sun Dec 31, 2023 4:58 am
mwoodage offline
User avatar
Posts: 174
Joined: Dec 19, 2014
Location: Devon UK

Re: Plugin help

Hi Neil, thanks for this,

I've added in #!/usr/bin/env python3 to the script and then ran chmod +x plugin.py in terminal, but this didn't return anything.

I then tried python3 chmod +x plugin.py and got an error.

So lastly tried python3 plugin.py and got some results.

It looks like there's a problem with the asyncio.get_event_loop().
I've tried googling this but don't really understand what it's doing, do you know?


Code: Select all
Last login: Sun Dec 31 10:12:30 on ttys001
/Users/martin/.zprofile:6: no such file or directory: /opt/homebrew/bin/brew
martin@Martins-Mac-mini-2 Server Plugin % chmod +x plugin.py
martin@Martins-Mac-mini-2 Server Plugin %
martin@Martins-Mac-mini-2 Server Plugin % python3 chmod +x plugin.py
/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python: can't open file '/Library/Application Support/Perceptive Automation/Scripts/Toyota/Server Plugin/chmod': [Errno 2] No such file or directory
martin@Martins-Mac-mini-2 Server Plugin %
martin@Martins-Mac-mini-2 Server Plugin %
martin@Martins-Mac-mini-2 Server Plugin % python3 plugin.py         
/Library/Application Support/Perceptive Automation/Scripts/Toyota/Server Plugin/plugin.py:111: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()
Logging in...
Retrieving cars...
Alias: JTMGBRFV80D148468
Traceback (most recent call last):
  File "/Library/Application Support/Perceptive Automation/Scripts/Toyota/Server Plugin/plugin.py", line 112, in <module>
    loop.run_until_complete(get_information())
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 664, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/Library/Application Support/Perceptive Automation/Scripts/Toyota/Server Plugin/plugin.py", line 88, in get_information
    print("Mileage "+mileage)
          ~~~~~~~~~~^~~~~~~~
TypeError: can only concatenate str (not "int") to str
martin@Martins-Mac-mini-2 Server Plugin %



Thanks for all your help on this so far :D
M

Posted on
Sun Dec 31, 2023 7:46 am
FlyingDiver offline
User avatar
Posts: 7222
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Plugin help

get_event_loop() has been deprecated in recent versions of Python. Instead, do this:
Code: Select all
loop = asyncio.new_event_loop()
loop.run_until_complete(get_information())
loop.close()

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Sun Dec 31, 2023 7:49 am
FlyingDiver offline
User avatar
Posts: 7222
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Plugin help

Also, this line is throwing an error:

Code: Select all
        print("Mileage "+mileage)


Should be:

Code: Select all
        print(f"Mileage {mileage}")


In general, use f-strings for all print or log statements, they're much more forgiving.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Sun Dec 31, 2023 8:19 am
neilk offline
Posts: 715
Joined: Jul 13, 2015
Location: Reading, UK

Re: Plugin help

OK, this will teach me to respond while reading in bed.

The "chmod" command only needs to be run once, it make the script executable, so now in the command line you should be able to just type "plugin.py" and not need the python in front of it.

Joe's pointed out the fix for the offending print statement (my bad, I thought it was returning strings not an integer) so I suspect you will also get another error when you fix that (I have corrected the issue and incorporated Joe's suggestion for the main loop) below.

Code: Select all
#!/usr/bin/env python3
import asyncio

# Adding the import line from the variable example from the new HTTP api "Updating a variables value" and removed the pretty print as it won't be needed
from urllib.request import Request, urlopen
import json
from mytoyota.client import MyT


# Again from the example add your details below, including the correct variable ID for mileage, fuel, battery and latitude
REFLECTORNAME = "YOUR-REFLECTOR-NAME"
APIKEY = "YOUR-API-KEY"
MILEAGEVARIABLE = 123456789
FUELVARIABLE = 123456789
BATTERYVARIABLE = 123456789
LATITUDEVARIABLE = 123456789


# now define a function (so we don't need to repeat the code to set each variable, we will call it later but it is the code from the http api python example

def set_indigo_variable(VARIABLEID, VALUE):
    message = json.dumps({
        "id": "optional-user-generated-id",
        "message": "indigo.variable.updateValue",
        "objectId": VARIABLEID,
        "parameters": {
            # this is modified to use VALUE that is passed to the function to updated the indigo variable rather than "Some string value" in the example code
            "value": VALUE
        }
    }).encode("utf8")
    req = Request(f"https://{REFLECTORNAME}.indigodomo.net/v2/api/command", data=message)
    req.add_header('Authorization', f"Bearer {APIKEY}")
    with urlopen(req) as request:
        reply = json.load(request)
        print(reply)



# Set your username and password here OR
# in a file called credentials.json in the format
#   {
#       "username": "<username>",
#       "password": "<password>"
#   }
username = '***********'
password = '***********'
try:
    credentials = json.load(open("credentials.json"))
    username = credentials["username"]
    password = credentials["password"]
except FileNotFoundError:
    pass
except json.decoder.JSONDecodeError:
    pass

if username is None or password is None:
    print(
        "Did you forget to set your username and password? Or supply the credentials file"
    )
    exit()

# Pretty indigo.server.loger used below

client = MyT(username=username, password=password, brand="T")


async def get_information():
    print("Logging in...")
    await client.login()

    print("Retrieving cars...")
    # Returns cars registered to your account + information about each car.
    cars = await client.get_vehicles()

    for car in cars:
        await car.update()

        # Alias
        print("Alias: "+car.alias)
        # Set alias
        # await car.set_alias("RAV4")

        # Basic information
        mileage = car.dashboard.odometer
        print(f"Mileage :{mileage}")
        # now call the function passing it the correct variable ID and the mileage value returned (Same approach for the others)
        set_indigo_variable(MILEAGEVARIABLE, str(mileage))
        # Or retrieve the energy level (electric or gasoline)
        fuel = car.dashboard.fuel_level
        print(f"Fuel :{fuel}")
        set_indigo_variable(FUELVARIABLE, str(fuel))
        battery = car.dashboard.battery_level
        print(f"Battery : {battery}")
        set_indigo_variable(BATTERYVARIABLE, str(battery))
        # Or Parking information:
        latitude = car.location.latitude
        print(f"Latitude :{latitude}")
        set_indigo_variable(LATITUDEVARIABLE, str(latitude))

        # Notifications => True retrieve all, False just unread
        # notifications = car.notifications(True)[:5]
        # if notifications:
        #  indigo.server.log("Notifications:")
        # for notification in notifications:
        #    indigo.server.log(f"    {notification.date} : {notification.message}")


loop = asyncio.new_event_loop()
loop.run_until_complete(get_information())
loop.close()


I wouldn't call this plugin.py and I would suggest creating a new directory in your home directory (/Users/martin) to keep this script in that directory.

so say you create a new 'toyota.py' in, once in that directory you will need to run 'chmod +x toyota.py' once, it will run without a response unless it errors (I should have told you that)

you can then in the terminal, in the new directory you have created be able to run it with './toyota.py'

If I haven't done anything else stupid we should be good !

Posted on
Sun Dec 31, 2023 9:15 am
mwoodage offline
User avatar
Posts: 174
Joined: Dec 19, 2014
Location: Devon UK

Re: Plugin help

Thanks Neil, Joe,
I've moved the file to a new location, renamed it toyota.py and replaced the code with the new one. It's new location is /Users/martin/toyota/toyota.py but I'm getting the following errors now.
I'm assuming this is because i've moved the file from it's original location? I'll try playing around with other locations to see what happens, but wanted to post this first.

Thanks again :D
Attachments
Screenshot 2023-12-31 at 14.43.36.png
Screenshot 2023-12-31 at 14.43.36.png (190.54 KiB) Viewed 688 times

Posted on
Sun Dec 31, 2023 9:20 am
FlyingDiver offline
User avatar
Posts: 7222
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Plugin help

Trying to make the script executable on it's own (not using python3 on the command line) is probably not worth the effort. But if you do want to, you need to put a line at the start of the script telling the shell what kind of executable file it is. The .py extension is not enough.

Put this as the first line of the script:

Code: Select all
#!/usr/bin/env python


It's in @neilk's version, but it's not in yours.

You should post the actual script you're using each time, since we don't know what changes you actually made since the last post. Also, screen shots of the terminal output are not that useful. Post the actual text. Again, using CODE tags.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Sun Dec 31, 2023 9:37 am
mwoodage offline
User avatar
Posts: 174
Joined: Dec 19, 2014
Location: Devon UK

Re: Plugin help

Thanks Joe, here's the script i'm using, without the python3 part, this however still give the same errors as before

Code: Select all
#!/usr/bin/env python
import asyncio

# Adding the import line from the variable example from the new HTTP api "Updating a variables value" and removed the pretty print as it won't be needed
from urllib.request import Request, urlopen
import json
from mytoyota.client import MyT


# Again from the example add your details below, including the correct variable ID for mileage, fuel, battery and latitude
REFLECTORNAME = "http://**********.indigodomo.net"
APIKEY = "**********************"
MILEAGEVARIABLE = 924494111
FUELVARIABLE = 1757439952
BATTERYVARIABLE = 1970916222
LATITUDEVARIABLE = 998494523


# now define a function (so we don't need to repeat the code to set each variable, we will call it later but it is the code from the http api python example

def set_indigo_variable(VARIABLEID, VALUE):
    message = json.dumps({
        "id": "optional-user-generated-id",
        "message": "indigo.variable.updateValue",
        "objectId": VARIABLEID,
        "parameters": {
            # this is modified to use VALUE that is passed to the function to updated the indigo variable rather than "Some string value" in the example code
            "value": VALUE
        }
    }).encode("utf8")
    req = Request(f"https://**************.indigodomo.net/v2/api/command", data=message)
    req.add_header('Authorization', f"Bearer {APIKEY}")
    with urlopen(req) as request:
        reply = json.load(request)
        print(reply)



# Set your username and password here OR
# in a file called credentials.json in the format
#   {
#       "username": "<username>",
#       "password": "<password>"
#   }
username = '****************'
password = '**************'
try:
    credentials = json.load(open("credentials.json"))
    username = credentials["username"]
    password = credentials["password"]
except FileNotFoundError:
    pass
except json.decoder.JSONDecodeError:
    pass

if username is None or password is None:
    print(
        "Did you forget to set your username and password? Or supply the credentials file"
    )
    exit()

# Pretty indigo.server.loger used below

client = MyT(username=username, password=password, brand="T")


async def get_information():
    print("Logging in...")
    await client.login()

    print("Retrieving cars...")
    # Returns cars registered to your account + information about each car.
    cars = await client.get_vehicles()

    for car in cars:
        await car.update()

        # Alias
        print("Alias: "+car.alias)
        # Set alias
        # await car.set_alias("RAV4")

        # Basic information
        mileage = car.dashboard.odometer
        print(f"Mileage :{mileage}")
        # now call the function passing it the correct variable ID and the mileage value returned (Same approach for the others)
        set_indigo_variable(MILEAGEVARIABLE, str(mileage))
        # Or retrieve the energy level (electric or gasoline)
        fuel = car.dashboard.fuel_level
        print(f"Fuel :{fuel}")
        set_indigo_variable(FUELVARIABLE, str(fuel))
        battery = car.dashboard.battery_level
        print(f"Battery : {battery}")
        set_indigo_variable(BATTERYVARIABLE, str(battery))
        # Or Parking information:
        latitude = car.location.latitude
        print(f"Latitude :{latitude}")
        set_indigo_variable(LATITUDEVARIABLE, str(latitude))

        # Notifications => True retrieve all, False just unread
        # notifications = car.notifications(True)[:5]
        # if notifications:
        #  indigo.server.log("Notifications:")
        # for notification in notifications:
        #    indigo.server.log(f"    {notification.date} : {notification.message}")


loop = asyncio.new_event_loop()
loop.run_until_complete(get_information())
loop.close()



Code: Select all
Last login: Sun Dec 31 15:28:33 on ttys000
/Users/martin/.zprofile:6: no such file or directory: /opt/homebrew/bin/brew
martin@Martins-Mac-mini-2 Totota % ./toyota.py       
./toyota.py: line 4: import: command not found
from: can't read /var/mail/urllib.request
./toyota.py: line 8: import: command not found
from: can't read /var/mail/mytoyota.client
./toyota.py: line 13: REFLECTORNAME: command not found
./toyota.py: line 14: APIKEY: command not found
./toyota.py: line 15: MILEAGEVARIABLE: command not found
./toyota.py: line 16: FUELVARIABLE: command not found
./toyota.py: line 17: BATTERYVARIABLE: command not found
./toyota.py: line 18: LATITUDEVARIABLE: command not found
./toyota.py: line 23: syntax error near unexpected token `('
./toyota.py: line 23: `def set_indigo_variable(VARIABLEID, VALUE):'
martin@Martins-Mac-mini-2 Totota %


Who is online

Users browsing this forum: No registered users and 14 guests