--- /dev/null
+DESCRIPTION = "AGL VSS helper daemon"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+SRC_URI = "file://agl-vss-helper.py \
+ file://agl-vss-helper.yaml \
+ file://agl-vss-helper.token \
+ file://agl-vss-helper.service \
+"
+
+inherit systemd
+
+SYSTEMD_SERVICE:${PN} = "${BPN}.service"
+
+do_configure[noexec] = "1"
+do_compile[noexec] = "1"
+
+do_install() {
+ install -d ${D}${sbindir}
+ install -m 0755 ${WORKDIR}/${BPN}.py ${D}${sbindir}
+ install -d ${D}${sysconfdir}/xdg/AGL/${BPN}
+ install -m 0644 ${WORKDIR}/${BPN}.yaml ${D}${sysconfdir}/xdg/AGL/
+ install -m 0644 ${WORKDIR}/${BPN}.token ${D}${sysconfdir}/xdg/AGL/${BPN}/
+ install -D -m 0644 ${WORKDIR}/${BPN}.service ${D}${systemd_system_unitdir}/${BPN}.service
+}
+
+RDEPENDS:${PN} = " \
+ python3 \
+ python3-asyncio \
+ python3-systemd \
+ kuksa-databroker \
+ kuksa-databroker-agl \
+ kuksa-client \
+ kuksa-certificates-agl-ca \
+"
--- /dev/null
+#!/usr/bin/env python3
+# Copyright (c) 2022 Aakash Solanki, tech2aks@gmail.com
+# Copyright (c) 2024 Scott Murray <scott.murray@konsulko.com>
+#
+# SPDX-License-Identifier: MIT
+
+import sys
+from pathlib import Path
+import yaml
+import asyncio
+import concurrent.futures
+from kuksa_client.grpc.aio import VSSClient
+from kuksa_client.grpc import Datapoint
+from systemd.daemon import notify
+
+# Defaults
+hostname = "localhost"
+port = 55555
+config_filename = "/etc/xdg/AGL/agl-vss-helper.yaml"
+token_filename = "/etc/xdg/AGL/agl-vss-helper/agl-vss-helper.token"
+ca_cert_filename = "/etc/kuksa-val/CA.pem"
+tls_server_name = "localhost"
+verbose = False
+
+async def main():
+ client = VSSClient(hostname,
+ port,
+ root_certificates=Path(ca_cert_filename),
+ tls_server_name=tls_server_name,
+ token=token,
+ ensure_startup_connection=True)
+ await client.connect()
+ print(f"Connected to KUKSA.val databroker at {hostname}:{port}")
+ if "initialize" in config and isinstance(config["initialize"], list):
+ for entry in config["initialize"]:
+ if "signal" in entry and "value" in entry:
+ if verbose:
+ print(f"Setting {entry['signal']} to {entry['value']}")
+ await client.set_current_values({ entry["signal"] : Datapoint(entry["value"]) })
+
+ notify("READY=1")
+
+ if "mock" in config and isinstance(config["mock"], list):
+ if len(config["mock"]) != 0:
+ print(f"Mocking actuators:")
+ for signal in config["mock"]:
+ print(f" {signal}")
+ async for updates in client.subscribe_target_values(config["mock"]):
+ for signal in updates:
+ if updates[signal] is not None:
+ if verbose:
+ print(f"Actuating {signal} to {updates[signal].value}")
+ await client.set_current_values({ signal : Datapoint(updates[signal].value) })
+
+
+#
+# Initialization
+#
+
+try:
+ config_file = open(config_filename, "r")
+ config = yaml.safe_load(config_file)
+except yaml.YAMLError as exc:
+ print(f"Could not parse configuration: ${exc}")
+except:
+ print(f"Could not read configuration")
+
+if "verbose" in config and isinstance(config["verbose"], bool):
+ verbose = config["verbose"]
+if "hostname" in config and isinstance(config["hostname"], string):
+ hostname = config["hostname"]
+if "port" in config and isinstance(config["port"], int):
+ port = config["port"]
+if "use-tls" in config and isinstance(config["use-tls"], bool):
+ use_tls = config["use-tls"]
+if "token-file" in config and isinstance(config["token-file"], string):
+ token_filename = config["token-file"]
+if "ca-certificate" in config and isinstance(config["ca-certificate"], string):
+ ca_cert_filename = config["ca-certificate"]
+
+if token_filename != "":
+ if verbose:
+ print(f"Reading authorization token {token_filename}")
+ token_file = open(token_filename, "r")
+ token = token_file.read()
+else:
+ token = ""
+
+print("Starting")
+try:
+ asyncio.run(main())
+except KeyboardInterrupt:
+ print("Exiting")
+
+notify("STOPPING=1")
+sys.exit(0)
--- /dev/null
+[Unit]
+Wants=network.target kuksa-databroker.service
+After=network.target kuksa-databroker.service
+
+[Service]
+Type=notify
+ExecStart=/usr/bin/python3 -u /usr/sbin/agl-vss-helper.py
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsb2NhbCBkZXYiLCJpc3MiOiJjcmVhdGVUb2tlbi5weSIsImF1ZCI6WyJrdWtzYS52YWwiXSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NjcyMjU1OTksInNjb3BlIjoiYWN0dWF0ZSBwcm92aWRlIn0.x-bUZwDCC663wGYrWCYjQZwQWhN1CMuKgxuIN5dUF_izwMutiqF6Xc-tnXgZa93BbT3I74WOMk4awKHBUSTWekGs3-qF6gajorbat6n5180TOqvNu4CXuIPZN5zpngf4id3smMkKOT699tPnSEbmlkj4vk-mIjeOAU-FcYA-VbkKBTsjvfFgKa2OdB5h9uZARBg5Rx7uBN3JsH1I6j9zoLid184Ewa6bhU2qniFt5iPsGJniNsKsRrrndN1KzthO13My44s56yvwSHIOrgDGbXdja_eLuOVOq9pHCjCtorPScgEuUUE4aldIuML-_j397taNP9Y3VZYVvofEK7AuiePTbzwxrZ1RAjK74h1-4ued3A2gUTjr5BsRlc9b7eLZzxLJkrqdfGAzBh_rtrB7p32TbvpjeFP30NW6bB9JS43XACUUm_S_RcyI7BLuUdnFyQDQr6l6sRz9XayYXceilHdCxbAVN0HVnBeui5Bb0mUZYIRZeY8k6zcssmokANTD8ZviDMpKlOU3t5AlXJ0nLkgyMhV9IUTwPUv6F8BTPc-CquJCUNbTyo4ywTSoODWbm3PmQ3Y46gWF06xqnB4wehLscBdVk3iAihQp3tckGhMnx5PI_Oy7utIncr4pRCMos63TnBkfrl7d43cHQTuK0kO76EWtv4ODEHgLvEAv4HA
\ No newline at end of file
--- /dev/null
+initialize:
+- signal: Vehicle.Speed
+ value: 0
+- signal: Vehicle.Powertrain.CombustionEngine.Speed
+ value: 600
+- signal: Vehicle.Powertrain.FuelSystem.RelativeLevel
+ value: 70
+- signal: Vehicle.Powertrain.FuelSystem.Range
+ value: 300000
+- signal: Vehicle.Powertrain.CombustionEngine.ECT
+ value: 70
+- signal: Vehicle.Body.Lights.DirectionIndicator.Left.IsSignaling
+ value: false
+- signal: Vehicle.Body.Lights.DirectionIndicator.Right.IsSignaling
+ value: false
+- signal: Vehicle.Powertrain.Transmission.SelectedGear
+ value: 127
+- signal: Vehicle.Body.Lights.Beam.Low.IsOn
+ value: true
+- signal: Vehicle.Body.Lights.Beam.High.IsOn
+ value: false
+- signal: Vehicle.Body.Lights.Parking.IsOn
+ value: false
+- signal: Vehicle.Body.Lights.Hazard.IsSignaling
+ value: false
+- signal: Vehicle.TraveledDistance
+ value: 1763
+- signal: Vehicle.TraveledDistanceSinceStart
+ value: 0
+- signal: Vehicle.Body.Trunk.Rear.IsLocked
+ value: true
+- signal: Vehicle.Body.Trunk.Rear.IsOpen
+ value: false
+- signal: Vehicle.Powertrain.Transmission.PerformanceMode
+ value: "NORMAL"
+- signal: Vehicle.Cabin.HVAC.AmbientAirTemperature
+ value: 22
+- signal: Vehicle.Exterior.AirTemperature
+ value: 24
+- signal: Vehicle.Chassis.Axle.Row1.Wheel.Left.Tire.Pressure
+ value: 220
+- signal: Vehicle.Chassis.Axle.Row1.Wheel.Right.Tire.Pressure
+ value: 216
+- signal: Vehicle.Chassis.Axle.Row2.Wheel.Left.Tire.Pressure
+ value: 217
+- signal: Vehicle.Chassis.Axle.Row2.Wheel.Right.Tire.Pressure
+ value: 222
+- signal: Vehicle.OBD.Status.IsMILOn
+ value: false
+- signal: Vehicle.Cabin.Infotainment.HMI.DistanceUnit
+ value: "KILOMETERS"
+- signal: Vehicle.Cabin.Infotainment.HMI.TemperatureUnit
+ value: "C"
+- signal: Vehicle.Cabin.Infotainment.HMI.TirePressureUnit
+ value: "KPA"
+- signal: Vehicle.ADAS.CruiseControl.IsEnabled
+ value: false
+- signal: Vehicle.ADAS.CruiseControl.IsActive
+ value: false
+- signal: Vehicle.ADAS.CruiseControl.SpeedSet
+ value: false
+- signal: Vehicle.ADAS.CruiseControl.IsError
+ value: false
+- signal: Vehicle.Powertrain.TractionBattery.Charging.IsCharging
+ value: false
+- signal: Vehicle.Powertrain.TractionBattery.Charging.IsChargingCableConnected
+ value: false
+- signal: Vehicle.Powertrain.TractionBattery.Charging.IsChargingCableLocked
+ value: false
+- signal: Vehicle.Powertrain.TractionBattery.Charging.IsDischarging
+ value: false
+#- signal: Vehicle.CurrentLocation.Latitude
+# value:
+#- signal: Vehicle.CurrentLocation.Longitude
+# value:
+#
+# AGL specific signals
+#
+- signal: Vehicle.Cabin.Infotainment.Navigation.State
+ value: "STOPPED"
+- signal: Vehicle.Cabin.Infotainment.Navigation.ElapsedDistance
+ value: 0.0
+#- signal: Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Latitude
+# value:
+#- signal: Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Longitude
+# value:
+- signal: Vehicle.Cabin.SteeringWheel.Switches.Info
+ value: false
+- signal: Vehicle.Cabin.SteeringWheel.Switches.VolumeUp
+ value: false
+- signal: Vehicle.Cabin.SteeringWheel.Switches.VolumeDown
+ value: false
+- signal: Vehicle.Cabin.SteeringWheel.Switches.VolumeMute
+ value: false
+- signal: Vehicle.Cabin.SteeringWheel.Switches.Next
+ value: false
+- signal: Vehicle.Cabin.SteeringWheel.Switches.Previous
+ value: false
+- signal: Vehicle.Cabin.SteeringWheel.Switches.Mode
+ value: false
+
+mock:
+- Vehicle.Cabin.Infotainment.HMI.DistanceUnit
+- Vehicle.Cabin.Infotainment.HMI.TemperatureUnit
+- Vehicle.Cabin.Infotainment.HMI.TirePressureUnit
+- Vehicle.ADAS.CruiseControl.IsEnabled
+- Vehicle.ADAS.CruiseControl.IsActive
+- Vehicle.ADAS.CruiseControl.SpeedSet
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright (c) 2022 Aakash Solanki, tech2aks@gmail.com
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy of
-# this software and associated documentation files (the "Software"), to deal in
-# the Software without restriction, including without limitation the rights to
-# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to do
-# so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-import kuksa_viss_client
-import time
-
-
-class VSS:
- def __init__(self, client):
- self.client = client
-
- self.speed = "Vehicle.Speed"
- self.engineRPM = "Vehicle.Powertrain.CombustionEngine.Speed"
- self.fuelLevel = "Vehicle.Powertrain.FuelSystem.Level"
- self.coolantTemp = "Vehicle.Powertrain.CombustionEngine.ECT"
- self.leftIndicator = "Vehicle.Body.Lights.IsLeftIndicatorOn"
- self.rightIndicator = "Vehicle.Body.Lights.IsRightIndicatorOn"
- # // Selected Gear output = > 0 = Neutral, 1/2/.. = Forward, -1/.. = Reverse, 126 = Park, 127 = Drive
- self.selectedGear = "Vehicle.Powertrain.Transmission.SelectedGear"
- self.lowBeamOn = "Vehicle.Body.Lights.IsLowBeamOn"
- self.highBeamOn = "Vehicle.Body.Lights.IsHighBeamOn"
- self.parkingLightOn = "Vehicle.Body.Lights.IsParkingOn"
- self.hazardLightOn = "Vehicle.Body.Lights.IsHazardOn"
- self.travelledDistance = "Vehicle.TravelledDistance"
- self.trunkLocked = "Vehicle.Body.Trunk.Rear.IsLocked"
- self.trunkOpen = "Vehicle.Body.Trunk.Rear.IsOpen"
- # // \"normal\", \"sport\", \"economy\", \"snow\", \"rain\"]
- self.performanceMode = "Vehicle.Powertrain.Transmission.PerformanceMode"
- self.ambientAirTemperature = "Vehicle.Exterior.AirTemperature"
- self.mil = "Vehicle.OBD.Status.IsMILOn"
- self.cruiseControlError = "Vehicle.ADAS.CruiseControl.IsError"
- self.cruiseControlSpeedSet = "Vehicle.ADAS.CruiseControl.SpeedSet"
- self.cruiseControlisActive = "Vehicle.ADAS.CruiseControl.IsActive"
- self.batteryChargingStatus = "Vehicle.Powertrain.TractionBattery.Charging.IsCharging"
- #
- self.currLat = "Vehicle.Cabin.CurrentLocation.Latitude"
- self.currLng = "Vehicle.Cabin.CurrentLocation.Longitude"
- self.desLat = "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Latitude"
- self.desLng = "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Longitude"
- self.steeringInfo = "Vehicle.Cabin.SteeringWheel.Switches.Info"
-
- def setInitialValues(self):
- print("Setting values")
- self.client.setValue(self.speed, '5')
- self.client.setValue(self.engineRPM, '1000')
- self.client.setValue(self.fuelLevel, '50')
- self.client.setValue(self.coolantTemp, '70')
- self.client.setValue(self.leftIndicator, "false")
- self.client.setValue(self.rightIndicator, "false")
- self.client.setValue(self.selectedGear, '127')
- self.client.setValue(self.lowBeamOn, "true")
- self.client.setValue(self.highBeamOn, "false")
- self.client.setValue(self.parkingLightOn, "true")
- self.client.setValue(self.hazardLightOn, "false")
- self.client.setValue(self.travelledDistance, '100')
- self.client.setValue(self.trunkLocked, "true")
- self.client.setValue(self.trunkOpen, "false")
- self.client.setValue(self.performanceMode, "normal")
- self.client.setValue(self.ambientAirTemperature, '28')
- self.client.setValue(self.mil, "false")
- self.client.setValue(self.cruiseControlError, "false")
- self.client.setValue(self.cruiseControlisActive, "false")
- self.client.setValue(self.cruiseControlSpeedSet, '60')
- self.client.setValue(self.batteryChargingStatus, "true")
- #
- self.client.setValue(self.currLat, "31.708643")
- self.client.setValue(self.currLng, "76.931882")
- self.client.setValue(self.desLat, "31.781456")
- self.client.setValue(self.desLng, "76.997469")
- # Show the map
- self.client.setValue(self.steeringInfo, "false")
- print("All value set succesfully")
-
-
-def main():
- config = {"ip": "localhost", "port": 8090, "insecure": False}
- client = kuksa_viss_client.KuksaClientThread(config)
- client.start()
- token_file = open(
- "/usr/lib/python3.10/site-packages/kuksa_certificates/jwt/all-read-write.json.token", "r")
- token = token_file.read()
- client.authorize(token, timeout=2)
-
- vss = VSS(client)
-
- time.sleep(2)
-
- vss.setInitialValues()
- client.stop()
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-DESCRIPTION = "Initialize the Kuksa VSS data to some constant values"
-LICENSE = "MIT"
-LIC_FILES_CHKSUM = "file://${WORKDIR}/kuksa_vss_init.py;beginline=2;endline=20;md5=afe8bd5e80449c5209495644133c16a8"
-
-SRC_URI = "file://kuksa_vss_init.py"
-
-do_configure[noexec] = "1"
-do_compile[noexec] = "1"
-
-do_install() {
- install -d ${D}${sbindir}
- install -m 0755 ${WORKDIR}/kuksa_vss_init.py ${D}${sbindir}
-}
-
-RDEPENDS:${PN} = "python3"
kuksa-databroker-agl \
kuksa-certificates-agl \
kuksa-dbc-feeder \
- kuksa-vss-init \
+ agl-vss-helper \
"
RDEPENDS:packagegroup-agl-kuksa-val-databroker-devel = "\